带返回的 Powershell 递归

Posted

技术标签:

【中文标题】带返回的 Powershell 递归【英文标题】:Powershell Recursion with Return 【发布时间】:2011-06-26 17:13:35 【问题描述】:

我正在尝试编写一个递归函数,该函数将返回数组中的信息,但是当我将 return 语句放入函数时,它会丢失某些条目。

我正在尝试以递归方式查看指定深度的文件夹,以获取与文件夹关联的 acl。我知道 getChildItem 有一个递归选项,但我只想逐步浏览 3 级文件夹。

下面的代码摘录是我一直用来测试的。如果在没有返回语句的情况下调用 getACLS(在下面注释掉),结果是:

文件夹 1

文件夹 12

文件夹 13

文件夹 2

当使用 return 语句时,我得到以下输出:

文件夹 1

文件夹 12

所以看起来返回语句正在退出递归循环?

我的想法是我想返回一个多维数组,例如 [文件夹名称、[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

【问题讨论】:

【参考方案1】:

问题在于退货的位置。我将它放在 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)


【讨论】:

【参考方案2】:

在递归中,我只会在需要结束递归的地方使用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

【讨论】:

【参考方案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)。

如果这是您要找的,请告诉我。我可以继续优化这个。

===========================================OLD==== ========================================== Return 将退出函数。

我在这里没有提供完整的解决方案,但想告诉你如何改变它。

您可以使用 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

这样,您可以在使用所需信息完全构建对象后返回该对象。

【讨论】:

不确定这是否可行,因为我需要嵌套对象。 递归函数需要能够返回不同的选项,具体取决于它是否是递归案例的基本案例。因此,如果 return 语句破坏了 powershell 中的递归,那么递归是不可能的吗?还是有不同类型的输出? 我可能误解了您的要求。让我回到剧本,再看一遍。我错过了递归部分,并给了你一个一般提示。递归可以在 PowerShell 中完成。【参考方案4】:

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

# 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)"
            
        
    

$arr = @() # have to define this outside the function and make it global
recursive C:\temp\test2 4
write-host $arr.count

【讨论】:

以上是关于带返回的 Powershell 递归的主要内容,如果未能解决你的问题,请参考以下文章

powershell 来自https://stackoverflow.com/questions/31051103/how-to-export-a-class-in-powershell-v5-mod

如何在 PowerShell 中输出内容

PowerShe 使用证书签名 ll脚本

带返回的递归方法

PowerShell启用多跳远程控制

PowerShell启用多跳远程控制