1

I am trying to implement some recursive functions with PowerShell. Here is the basic function:

function MyRecursiveFunction {
    param(
        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $input
    )

    if ($input -is [System.Array] -And $input.Length -eq 1) {
        $input = $input[0]
    }

    if ($input -is [System.Array]) {
        ForEach ($i in $input) {
            $i | ##### HOW DO I USE $MyInvocation HERE TO CALL MyRecursiveFunction??? #####
        }
        return
    }

    # Do something with the single object...
}

I have looked at Invoke-Expression and Invoke-Item but have not been able to get the syntax right. For instance I tried

$i | Invoke-Expression $MyInvocation.MyCommand.Name

I'm guessing there is an easy way to do this if you know the right syntax :-)

Josh Petitt
  • 9,371
  • 12
  • 56
  • 104
  • 2
    I suggest to change `$input` with another variable name. `$input` is a reserved and autopopulate variable in powershell script: http://stackoverflow.com/a/885651/520612 – CB. Nov 13 '14 at 14:58
  • @CB. point taken, thanks. I was actually hoping to hide the automatic variable with my variable, but in retrospect, perhaps this is not a good idea. – Josh Petitt Nov 13 '14 at 15:07

2 Answers2

1

A bit of an old question, but since there's no satisfactory answer and I've had the same question, here's my experience.

Calling the function by name will break if the name is changed, or if the function is out of scope. It's also not very nice to manage as changing the function name requires editing all the recursive calls, and it'll likely break in modules imported using the prefix option.

# A simple recursive countdown function
function countdown {
    param([int]$count)
    $count
    if ($count -gt 0) { countdown ($count - 1) }
}

# And a way to break it
$foo = ${function:countdown}
function countdown { 'failed' }
& $foo 5

$MyInvocation.InvocationName is slightly nicer to work with, but will still break in the above example (although for different reasons).


The best way would seem to be calling the scriptblock of the function, $MyInvocation.MyCommand.ScriptBlock. That way it'll still work regardless of the function name/scope.

function countdown {
    param([int]$count)
    $count
    if ($count -gt 0) { & $MyInvocation.MyCommand.ScriptBlock ($count - 1) }
}
Dabombber
  • 436
  • 3
  • 8
0

Just Call the function:

$i | MyRecursiveFunction

To call it without knowing the name of the function you should be able to call it with $myInvocation.InvocationName:

Invoke-Expression "$i | $($myInvocation.InvocationName)"
EBGreen
  • 36,735
  • 12
  • 65
  • 85
  • this is a perfectly valid solution. However, I have more than one function that I am implementing. Each time, I cut and paste the boilerplate code to deal with feeding in arrays as single objects, I have to remember to change the one line to the new function name. This has caused me problems a couple of times when I forget to change it. I am not a PS expert and there might be a better approach in general, but for now, I was hoping to use the $MyInvocation to make the boilerplate more general. – Josh Petitt Nov 13 '14 at 15:06
  • Just as an FYI, in general if you repeatedly use the same boilerplate code, you can probably generalize your code even further. – EBGreen Nov 13 '14 at 15:23