带 Return 的 Powershell 递归

问题描述 投票:0回答:4

我正在尝试编写一个递归函数,它将返回数组中的信息,但是当我将 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
function powershell recursion return
4个回答
8
投票

问题在于退货地点。我将它放在 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)
}
}

3
投票

在递归中,我只会在需要结束递归的地方使用

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

2
投票

更新:我将按原样保留第一个答案,并在此处添加新代码。我发现您的代码存在多个问题。这是更新的。

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
}

通过这种方式,一旦使用您需要的信息完全构建了对象,您就可以返回该对象。


0
投票

我发现这些解决方案都没有达到我需要的效果,即拥有一个包含整个递归函数结果的数组。由于我们不能在函数内部初始化数组,否则每次使用递归时都会重新初始化,所以我们必须在外部定义它并使用全局:

# 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
© www.soinside.com 2019 - 2024. All rights reserved.