【问题标题】:Folder Tree with NTFS Permissions具有 NTFS 权限的文件夹树
【发布时间】:2019-03-06 17:47:46
【问题描述】:

我正在尝试使用 PowerShell 构建一个小脚本,但我被卡住了。

我需要将文件夹的结构作为具有 NTFS 权限的树给出。

C:\测试 ├───Test1 - 访问 │ ├───Test1.1 - 访问 │ └───Test1.2 - 访问 │ └───Test1.2.1 - 访问 ├───Test2 - 访问 │ ├───Test2.1 - 访问 │ └───Test2.2 - 访问 ├───Test3 - 访问 └───Test4 - 访问 ├───Test4.1 - 访问 ├───Test4.2 - 访问 └────Test4.3 - 访问

类似的东西。

我尝试使用Get-ChidlItem C:\Test -RecurseGet-Acl,但我不知道如何将结果显示为树。

【问题讨论】:

  • tree 实用程序不允许列出权限。有许多方法可以为您提供列表视图 (see here),但如果您必须拥有树视图,您可能需要自己编写。几年前我写过something similar in VBScript,但是在PowerShell中写应该比这简单得多。
  • 这是一个有趣的想法,如果我有时间,我可能会研究它。基本上,您需要两个递归执行的函数。可以将 GCI 输出转换为标准的 ps 对象结构,其中 noteproperties 定义了该结构。下一个实际上会将第一个函数的输出写入树视图。我想也许网上会有类似的东西,但没有运气。

标签: powershell tree treeview ntfs


【解决方案1】:

下面是我最近编写的一个脚本,用于将目录结构作为具有文件大小的树输出到控制台。

您可以轻松修改Write-Host 行以删除文件大小并添加您的ACL 信息(我不太确定这将如何工作)。

使用Write-Host 的最大限制是您无法在脚本的后面处理数据,您无法将数据输出到文件 - Start-Transcript 会丢失格式。

Jacob Colvin 的 cmets 描述了一种更好的方法,这是我有兴趣在实践中看到的。

我能想到的唯一其他方法是创建一个组合Get-ACLGet-ChildItem 数据的对象,然后在输出数据时,计算目录结构中的\ 以生成缩进。

function Show-Tree
{
    param
    (
        $directory
    )
    if ((Get-PSCallStack)[1].Command -eq 'Show-Tree') {
        Write-host "├──" -NoNewline
        if(Test-Path $directory/*)
        {
            Write-host "┬" -NoNewline
        }
        else
        {
            Write-host "─" -NoNewline
        }
        Write-Host "$($directory | Split-Path -Leaf)/"
    }
    else
    {
        Write-Host "$directory"
        $Script:StartingIndent = ($directory).Split("\").Count+1
    }
    Get-ChildItem -path $directory -File | % { 
        $i = 0
        $indent = ($_.FullName).Split("\").Count-$script:StartingIndent
        while($i -lt $indent)
        {
            [console]::Write("│  ")
            $i++
        }
        if(($_.length / 1MB) -ge 1)
        {
            Write-Host "├───[$([int]($_.length / 1MB)) MB] $($_.name)"
        }
        elseif(($_.length / 1KB -ge 1))
        {
            Write-Host "├───[$([int]($_.length / 1KB)) KB] $($_.name)"
        }
        else
        {
            Write-Host "├───[$($_.length) B] $($_.name)"
        }
    }
    Get-ChildItem -Path $directory -Directory | % {
        $i = 0
        $indent = ($_.FullName).Split("\").Count-$script:StartingIndent
        while($i -lt $indent)
        {
            Write-host "│  " -NoNewline
            $i++
        }
        Show-Tree -directory $_.FullName
    }
}

Show-Tree -directory C:\Testing

输出

C:\Testing
├───[7 KB] $RY3NETI.prop
├───[9 KB] Book1.xlsx
├───[308 B] csvOut.csv
├───[308 B] csvOut2.csv
├───[297 B] csvout3.csv
├───[6 MB] Shakespear.txt
├───[570 KB] ShakeSpeareUniqueWords.txt
├───[1 KB] Transcript.txt
├──┬Test1/
│  ├───[183 B] filename.rtf
│  ├───[7 KB] img3.png
│  ├───[18 KB] img4.png
│  ├───[43 KB] img5.png
│  ├───[19 KB] img7.png
│  ├───[26 KB] test.doc
├──┬Test2/
│  ├───[83 MB] filetest.rar
│  ├───[7 KB] img10.png
│  ├───[18 KB] img9.png
│  ├──┬Test2 Subfolder/
│  │  ├───[33 KB] img1.png
│  │  ├───[17 KB] img2.png
│  │  ├───[14 KB] img8.png

【讨论】:

  • 有兴趣的可以看看我的回答。
【解决方案2】:

我发现自己对这个问题很好奇,并开始研究一些东西。有很多问题,但我想发布“总体思路”。

首先获取 FullNames 并将 ACL 信息附加到文件名:

$x = (Get-ChildItem C:\Users\j309921\Desktop\ -Recurse).FullName | ForEach-Object {
  "$_ - $((Get-Acl -Path $_).Access.AccessControlType | Select-Object -Unique)"
}

由于某种原因,我发现 GCI 并不总是返回所有路径。我还没有调查过这种行为。总之……

接下来我创建了这个函数来分离路径并将它们放入对象中。我认为这可以被替换以提高速度,但它使接下来的部分更容易处理。

function Convert-GCItoObject {
  param(
    [Parameter(Mandatory, ValueFromPipeline, HelpMessage='Items to process')]$InputObject
  )
  process {
    $output = @()
    $InputObject -split '\\' | ForEach-Object{ $output += $_ }
    for($i=$output.Count-1; $i -gt 0; $i--){
      $output[$i-1] = [pscustomobject]@{$output[$i-1] = $output[$i]}
    }
    $output[0]
  }
}

$x = $x | Convert-GCItoObject

然后我创建了我的第一个递归函数,它根据最后一个函数返回的路径构建一个对象树。如果有目录,它会重新运行该功能。这样你就可以从下往上制作对象了。

function Recurse-GCIObject {
  param(
    [Parameter(Mandatory, HelpMessage='Items to process')]$object
  )
  $pscustomobject = [pscustomobject]@{}
  $object | Where-Object {$_} | Where-Object {$_.GetType().Name -ne 'String'} | ForEach-Object {$_.psobject.properties.name} | Select-Object -Unique | ForEach-Object {
    if($object.$_) {
      $pscustomobject | Add-Member @{$_ = $(Recurse-GCIObject -object $object.$_) }
    }
  }
  try {
    $pscustomobject | Add-Member -Name Contains -Value ($object | Where-Object {$_.GetType().Name -eq 'String' -and -not $pscustomobject.$_}) -MemberType NoteProperty -ErrorAction Stop
  } catch {
    Write-Verbose 'Empty'
  }
  $pscustomobject
}

$x = Recurse-GCIObject -object $x

此时该对象非常可用。我做了一个快速函数来制作漂亮的树。我没有花太多时间在这上面,但它是可读的。

function Out-Tree {
  param(
    [Parameter(Mandatory, ValueFromPipeline, HelpMessage='Items to process')]$InputObject, $lvl = 0
  )
  process {
    if($lvl -gt 0) { $tab = (0..$lvl | ForEach-Object {"`t"}) } else {$tab = ''}
    $InputObject | Where-Object {$_} | ForEach-Object {$_.psobject.properties.name} | ForEach-Object {
      if ($_ -ne 'Contains') {
        "$tab+-- $_ `n"
        if($InputObject.$_) {
          "$($InputObject.$_ | Out-Tree -lvl ($lvl + 1) )"
        }
      }
    }
    $InputObject.Contains | Where-Object {-not [string]::IsNullOrEmpty($_)} | ForEach-Object { "$tab|`t|-- $_ `n" }
  }
}

$x | Out-Tree

这有很多问题。它非常慢,GCI 输出有时不会显示所有正确的信息,而且树视图可能会好很多。但我想将其发布为更多的 POC/练习。以后我可能会改进它并邀请其他人来贡献。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-16
    • 2010-10-28
    • 1970-01-01
    • 2016-03-13
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2015-04-17
    相关资源
    最近更新 更多