1

I have the following two scripts:

### main.ps1
. .\inner.ps1 $args[0] $myInvocation.MyCommand.Definition
echo "second call"
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

and

### inner.ps1
if( $($args[0]) -ne "secondRun") {
    echo "first"
    echo  $($args[1])
    & $($args[1]) "secondRun"
    exit    
}

The exit command in inner.ps1 does not exit from the caller script main.ps1, so I hit twice the second call code.

Can I exit from the caller too?
How can I obtain proper recursion?
I would like to put all/most of the burden of recursion in inner.ps1 and keep main.ps1 as clean as possible, which means avoiding adding in main.ps1 a line like:

if( $($args[0]) -ne "secondRun") {exit;}
0

1 Answer 1

2

Even with dot-sourcing (.), a script cannot instruct its caller to exit, so in your current setup you need some conditional in main.ps1 that checks for the need to exit after the . .\inner.ps1 call.

Given that helper script inner.ps1 is being dot-sourced and can therefore modify the environment of the caller, main.ps1, one wonders why recursion is necessary altogether.
The remainder of this answer accepts the premise of the question, however.

The OP himself came up with a solution that encapsulates the recursive-invocation details inside inner.ps1, but the problem is that using the success stream (the regular output stream) to send control information - $TRUE or $FALSE - means that the if statement that checks that information (if (. .\inner.ps1) ...) consumes the regular output from main1.ps1 and therefore suppresses it.[1]

In other words: the approach only works if main.ps1 happens to produce no output at all, or the caller happens not to care what main.ps1 outputs.

The solution is to base the control flow on exit codes (which PowerShell reflects in automatic variable $LASTEXITCODE):

### inner.ps1

if(-not $__inner_already_recursed) {

  'inner: reinvoking main...'
  $__inner_already_recursed=$true  # set flag to indicate that recursion has happened
  . $myInvocation.ScriptName       # re-invoke main.ps1

  exit 0 # Signal need to continue execution with $LASTEXITCODE 0

} else {

  'inner: not reinvoking...'  
  exit 1 # Signal that this is already the 2nd invocation.

}

and:

### main.ps1

'main: entering'

# Call the helper script that performs the recursive invocation.
. .\inner.ps1
if ($LASTEXITCODE -eq 1) { exit }  # 1 means previously already reinvoked -> exit

# We only get here once, in the recursive invocation.
'main: should only get here once'

As the OP states, the auxiliary variable used to maintain the recursive-invocation state in inner.ps1 must not preexist (or at least not with a truthy value), so its name - $__inner_already_recursed - was chosen to minimize that risk.


[1] Write-Host calls aren't affected, because they print directly to the console, but their output can neither be captured nor sent through the pipeline.
(With additional effort, you could in PSv5+, but that's generally ill-advised and not helpful here.)

Sign up to request clarification or add additional context in comments.

1 Comment

So much effort. Nice weekend friendly user :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.