Powershell Recursion с возвратом - PullRequest
5 голосов
/ 14 февраля 2011

Я пытаюсь написать рекурсивную функцию, которая будет возвращать информацию в массиве, однако, когда я помещаю инструкцию return в функцию, она пропускает определенные записи.

Я пытаюсь рекурсивно просматривать заданную глубинупапок, получающих ACL, связанные с папкой.Я знаю, что getChildItem имеет опцию recurse, но я хочу пройти только через 3 уровня папок.

Ниже приведен фрагмент кода, который я использовал для тестирования.Когда getACLS вызывается без оператора возврата (закомментировано ниже), получаются следующие результаты:

Папка 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

Ответы [ 3 ]

7 голосов
/ 15 февраля 2011

Проблема была в расположении возврата.У меня это было внутри цикла 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 голосов
/ 14 февраля 2011

В рекурсии я бы использовал оператор 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 голосов
/ 14 февраля 2011

Обновление: я оставляю первый ответ как есть и добавляю новый код здесь. Я вижу, что есть несколько проблем с вашим кодом. вот обновленный.

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

Если вы видите выше, я не использую возврат. Я просто выбрасываю объект из функции GetACLs. Кроме того, я изменил его, чтобы вернуть $ security для целей тестирования. Я могу видеть все ACL в $ результатов. Я изменил первое условие if на if ($ current -lt $ max). Не должно быть, если ($ current -le $ max).

Дайте мне знать, если это то, что вы ищете. Я могу продолжать оптимизировать это.

========================================== OLD ==== ========================================= Возврат приведет к выходу из функции.

Я не предоставляю здесь полное решение, но хочу дать вам представление о том, как это можно изменить.

Вы можете использовать PS Custom object для сбора необходимой вам информации. Например,

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
}

Таким образом, вы можете вернуть объект, как только он будет полностью построен с необходимой вам информацией.

...