我正在尝试编写一个递归函数,它将返回数组中的信息,但是当我将 return 语句放入函数中时,它会丢失某些条目。
我正在尝试递归地查看指定深度的文件夹,获取与该文件夹关联的 acl。我知道 getChildItem 有一个递归选项,但我只想单步执行 3 层文件夹。
下面的代码摘录是我一直用于测试的代码。当调用 getACLS 而不使用 return 语句(下面注释掉)时,结果是:
文件夹1
文件夹 12
文件夹13
文件夹2
使用 return 语句时,我得到以下输出:
文件夹1
文件夹 12
那么看起来 return 语句正在从递归循环中退出?
我的想法是我想返回一个多维数组,例如 [文件夹名称,[acls],[[子文件夹,[权限],[[...]]]]] 等
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = @()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -le $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
Write-Host $dir.Name
# return ($newPath, $security, getACLS $newPath $max ($current+1))
# getACLS $newPath $max ($current+1)
return getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return ($path, $security)
}
}
$results = getACLS "PATH\Testing" 2 0
问题在于退货地点。我将它放在 foreach 循环中,这意味着它试图在一个函数中多次返回。我将它移到了 foreach 之外,移到了 if 语句中。
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = @()
$results = @()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
$next = $current + 1
$results += (getACLS $newPath $max $next)
}
} else {
$results = ($path, $security)
}
return ($path, $security, $results)
} elseif ($current -eq $max ) {
return ($path, $security)
}
}
在递归中,我只会在需要结束递归的地方使用
return
语句 - 只是为了清楚起见。我在 PowerShell 中做了很多递归,效果很好。但是您需要记住,PowerShell 函数的行为确实有所不同。以下:
return 1,2
相当于:
1,2
return
换句话说,任何未在变量中捕获或重定向到文件(或 $null)的内容都会自动被视为函数的输出。这是一个有效的递归函数的简单示例:
function recursive($path, $max, $level = 1)
{
$path = (Resolve-Path $path).ProviderPath
Write-Host "$path - $max - $level"
foreach ($item in @(Get-ChildItem $path))
{
if ($level -eq $max) { return }
recursive $item.PSPath $max ($level + 1)
}
}
recursive ~ 3
更新:我将按原样保留第一个答案,并在此处添加新代码。我发现您的代码存在多个问题。这是更新的。
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = @()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $dir.FullName
$security
getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return $security
}
}
$results = getACLS "C:\Scripts" 2 0
如果你看到上面的内容,我没有使用 return。我只是从 GetACLs 函数中抛出该对象。另外,我将其修改为返回 $security 以用于测试目的。我可以在 $results 中看到所有 ACL。我将第一个 if 条件更改为 if ($current -lt $max)。不应该是 if ($current -le $max)。
请告诉我这是否是您正在寻找的。我可以继续优化这个。
============================================旧====== ========================================= 返回将退出该功能。
我不在这里提供完整的解决方案,但想让您了解如何更改此问题。
您可以使用PS自定义对象来捕获您需要的信息。例如,
function GetItem {
$itemsArray = @()
Get-ChildItem C:\Scripts | ForEach-Object {
$itemsObject = New-Object PSObject
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "FullName" -Value $_.FullName
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "Name" -Value $_.Name
$itemsArray += $itemsObject
}
return $itemsArray
}
通过这种方式,一旦使用您需要的信息完全构建了对象,您就可以返回该对象。
我发现这些解决方案都没有达到我需要的效果,即拥有一个包含整个递归函数结果的数组。由于我们不能在函数内部初始化数组,否则每次使用递归时都会重新初始化,所以我们必须在外部定义它并使用全局:
# Get folder contents recursively and add to an array
function recursive($path, $max, $level = 1)
{
Write-Host "$path" -ForegroundColor Red
$global:arr += $path
foreach ($item in @(Get-ChildItem $path))
{
if ($level -eq $max) { return }
if ($item.Length -eq "1") # if it is a folder
{
$newpath = "$path\$($item.Name)"
recursive $newpath $max ($level + 1)
}
else { # else it is a file
Write-Host "$path\$($item.Name)" -ForegroundColor Blue
$global:arr += "$path\$($item.Name)"
}
}
}
$global:arr = @() # have to define this outside the function and make it global
recursive C:\temp\test2 4
write-host $global:arr.count