Powershell Recursion with Return Powershell Recursion with Return powershell powershell

Powershell Recursion with Return


The problem was the location of the return. I had it inside the foreach loop, meaning it was trying to return multiple times in the one function. I moved it outside the foreach, into the if statement instead.

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)}}


In recursion, I would only use a return statement where I needed to end the recursion - just for clarity. I've done a good bit of recursion in PowerShell and it works well. However you need to remember that PowerShell functions do behave differently. The following:

return 1,2

is equivalent to:

1,2return

In other words, anything you don't capture in a variable or redirect to a file (or $null) is automatically considered output of the function. Here's a simple example of a working, recursive function:

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


Update: I am leaving the first answer as is and adding the new code here. I see that there are multiple issues with your code. here is the updated one.

clsfunction 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

If you see above, I am not using return. I just throw the object from the GetACLs function. Also, I modified it to return on $security for testing purpose. I can see the all ACLs in $results. I changed the first if condition to if ($current -lt $max). It should not be if ($current -le $max).

Let me know if this what you are looking for. I can continue to optimize this.

==========================================OLD=============================================Return will exit the function.

I am not providing the complete solution here but want to give you an idea about how this can be changed.

You can use PS Custom object to capture the information you need. For example,

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}

This way you can return the object once it is completely built with the information you need.