2011-02-14 9 views
5

Estoy intentando escribir una función recursiva que devolverá información en una matriz, sin embargo, cuando coloco una declaración de devolución en la función, falla ciertas entradas.Recursividad de Powershell con devolución

Estoy tratando de buscar recursivamente a través de una profundidad específica de carpetas obteniendo los acl asociados a la carpeta. Sé que getChildItem tiene una opción recurrente, pero solo quiero pasar por 3 niveles de carpetas.

El extracto del código siguiente es lo que he estado utilizando para probar. Cuando getACLS se llama sin una instrucción de retorno (comentado más adelante), los resultados son:

Carpeta 1

carpeta 12

carpeta 13

Carpeta 2

Cuando la instrucción de retorno es Utilizo la siguiente salida:

Carpeta 1

Carpeta 12

¿Parece que la declaración de retorno está saliendo del bucle recursivo?

La idea es que quiero devolver una matriz multidimensional como [nombre de la carpeta, [ACL], [[subcarpeta, [permisos], [[...]]]]] etc.

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 

Respuesta

7

El problema fue la ubicación de la devolución. Lo tenía dentro del ciclo foreach, lo que significaba que intentaba regresar varias veces en una función. Lo moví fuera del foreach, en la declaración if en su lugar.

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

Actualización: estoy dejando la primera respuesta tal como está y agregando el nuevo código aquí. Veo que hay múltiples problemas con tu código. aquí está el actualizado.

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 

Si ves arriba, no estoy utilizando el retorno. Solo tiro el objeto de la función GetACLs. Además, lo modifiqué para volver a $ security con fines de prueba. Puedo ver todas las ACL en $ resultados. Cambié la primera condición if a if ($ current -lt $ max). No debería ser si ($ current -le $ max).

Avísame si esto es lo que está buscando. Puedo continuar optimizando esto.

==================================== ANTIGUO === =========================== El regreso saldrá de la función.

No proporciono la solución completa aquí, pero quiero darle una idea de cómo se puede cambiar esto.

Puede usar el objeto personalizado de PS para capturar la información que necesita. Por ejemplo,

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 
} 

De esta forma puede devolver el objeto una vez que esté completamente construido con la información que necesita.

+0

No estoy seguro de que funcione porque tendría que anidar los objetos. – Rumpleteaser

+0

Una función recursiva debe poder devolver diferentes opciones dependiendo de si se trata de un caso base de un caso recursivo. Entonces, si una declaración de retorno interrumpe la recursión en powershell, ¿la recursión simplemente no es posible? o hay un tipo diferente de salida? – Rumpleteaser

+0

Podría haber malinterpretado sus requisitos. Permítanme volver al guión y echarle un vistazo nuevamente. Me perdí la parte de recursividad y te di un consejo general. La recursión se puede hacer en PowerShell. – ravikanth

3

En recursividad, solo usaría una declaración return en la que necesitaba finalizar la recursión, solo por claridad. He hecho una buena recursión en PowerShell y funciona bien. Sin embargo, debe recordar que las funciones de PowerShell se comportan de manera diferente.Los siguientes:

return 1,2 

es equivalente a:

1,2 
return 

En otras palabras, cualquier cosa que no captura en una variable o redirigir a un archivo (o $ null) se considera automáticamente la salida de la función. He aquí un ejemplo sencillo de una función de trabajo, recursiva:

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