具有 NTFS 权限的文件夹树
Folder Tree with NTFS Permissions
我正在尝试使用 PowerShell 构建一个小脚本,但我被卡住了。
我需要将文件夹的结构给出为具有 NTFS 权限的树。
C:\TEST
├───Test1 - Access
│ ├───Test1.1 - Access
│ └───Test1.2 - Access
│ └───Test1.2.1 - Access
├───Test2 - Access
│ ├───Test2.1 - Access
│ └───Test2.2 - Access
├───Test3 - Access
└───Test4 - Access
├───Test4.1 - Access
├───Test4.2 - Access
└───Test4.3 - Access
像这样。
我试过 Get-ChidlItem C:\Test -Recurse
和 Get-Acl
,但我不知道如何将结果显示为树。
下面是我最近写的一个脚本,用于将目录结构作为树输出到控制台。
您可以轻松修改 Write-Host
行以删除文件大小并添加您的 ACL 信息(我不太确定这在美学上是如何工作的)。
使用 Write-Host
的最大限制是您无法在脚本中进一步处理数据,无法将数据输出到文件 - Start-Transcript
会丢失格式。
Jacob Colvin 的评论描述了一种更好的方法,我有兴趣在实践中看到它。
我能想到的唯一其他方法是创建一个结合了 Get-ACL
和 Get-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
我发现自己对这个问题很好奇并开始着手研究。有很多问题,但我想 post "general idea"。
首先获取全名并将 ACL 信息附加到文件名:
$x = (Get-ChildItem C:\Users\j309921\Desktop\ -Recurse).FullName | ForEach-Object {
"$_ - $((Get-Acl -Path $_).Access.AccessControlType | Select-Object -Unique)"
}
出于某种原因,我发现 GCI 并不总是 return 所有路径。我还没有研究过这种行为。无论如何...
接下来我做了这个函数来分离路径并将它们放入对象中。我认为这可以代替以提高速度,但它使接下来的部分更容易使用。
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
然后我创建了我的第一个递归函数,该函数根据最后一个函数 return 编辑的路径构建对象树。如果有目录,它会重新运行该函数。因此,您可以自下而上地制作对象。
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 输出有时不会显示所有正确的信息,而且树视图可能会好很多。但我想 post 这更像是 POC/exercise。稍后我可能会改进它并邀请其他人做出贡献。
我正在尝试使用 PowerShell 构建一个小脚本,但我被卡住了。
我需要将文件夹的结构给出为具有 NTFS 权限的树。
C:\TEST ├───Test1 - Access │ ├───Test1.1 - Access │ └───Test1.2 - Access │ └───Test1.2.1 - Access ├───Test2 - Access │ ├───Test2.1 - Access │ └───Test2.2 - Access ├───Test3 - Access └───Test4 - Access ├───Test4.1 - Access ├───Test4.2 - Access └───Test4.3 - Access
像这样。
我试过 Get-ChidlItem C:\Test -Recurse
和 Get-Acl
,但我不知道如何将结果显示为树。
下面是我最近写的一个脚本,用于将目录结构作为树输出到控制台。
您可以轻松修改 Write-Host
行以删除文件大小并添加您的 ACL 信息(我不太确定这在美学上是如何工作的)。
使用 Write-Host
的最大限制是您无法在脚本中进一步处理数据,无法将数据输出到文件 - Start-Transcript
会丢失格式。
Jacob Colvin 的评论描述了一种更好的方法,我有兴趣在实践中看到它。
我能想到的唯一其他方法是创建一个结合了 Get-ACL
和 Get-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
我发现自己对这个问题很好奇并开始着手研究。有很多问题,但我想 post "general idea"。
首先获取全名并将 ACL 信息附加到文件名:
$x = (Get-ChildItem C:\Users\j309921\Desktop\ -Recurse).FullName | ForEach-Object {
"$_ - $((Get-Acl -Path $_).Access.AccessControlType | Select-Object -Unique)"
}
出于某种原因,我发现 GCI 并不总是 return 所有路径。我还没有研究过这种行为。无论如何...
接下来我做了这个函数来分离路径并将它们放入对象中。我认为这可以代替以提高速度,但它使接下来的部分更容易使用。
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
然后我创建了我的第一个递归函数,该函数根据最后一个函数 return 编辑的路径构建对象树。如果有目录,它会重新运行该函数。因此,您可以自下而上地制作对象。
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 输出有时不会显示所有正确的信息,而且树视图可能会好很多。但我想 post 这更像是 POC/exercise。稍后我可能会改进它并邀请其他人做出贡献。