Powershell:递归脚本调用

时间:2017-01-27 14:25:51

标签: powershell recursion

我有以下两个脚本:

### 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")

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

exit中的inner.ps1命令未从调用者脚本main.ps1退出,因此我点击了第二次调用代码的两倍。

我也可以退出来电吗? 我怎样才能获得正确的递归? 我想将inner.ps1中的所有/大部分递归负担放在尽可能干净的main.ps1中,这意味着避免在main.ps1中添加如下行:

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

2 个答案:

答案 0 :(得分:2)

即使使用点源(.),脚本也无法指示其来电者exit ,因此在您当前的设置中,您需要某些条件main.ps1,检查. .\inner.ps1来电后是否需要退出。

鉴于辅助脚本inner.ps1是点源的,因此可以修改调用者的环境main.ps1,人们想知道为什么递归是完全必要的。
然而,这个答案的其余部分接受了问题的前提。 功能

OP自己提出a solution that encapsulates the recursive-invocation details inside inner.ps1,但问题是使用成功流(常规输出流)发送控制信息 - $TRUE或{{ 1}} - 表示检查该信息($FALSEif语句消耗 if (. .\inner.ps1) ...的常规输出,因此抑制它。 [1]

换句话说:该方法仅在main1.ps1碰巧根本不产生输出时才有效,或者调用者不关心main.ps1输出

解决方案是将控制流基于退出代码 (PowerShell在自动变量main.ps1中反映):

$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.

}

正如OP所述,用于维护### 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' 中的递归调用状态的辅助变量不能预先存在(或者至少不具有真值),因此其名称 - inner.ps1 - 是选择将风险降至最低。

[1] $__inner_already_recursed电话不受影响,因为它们直接打印到控制台,但它们的输出既不能被捕获也不能通过管道发送。
  (通过额外的努力,你可以在PSv5 +中,但这通常是不明智的,在这里没有帮助。)

答案 1 :(得分:-1)

这种方法对来电者来说似乎很轻松。

### inner.ps1
if( -not $recurseScript ) {
    write-host "first"
    write-host $myInvocation.ScriptName
    $recurseScript=$TRUE
    & $myInvocation.ScriptName
    return $TRUE
}

$recurseScript=$FALSE
return $FALSE

要使main.ps1递归,现在我只需要行if(. .\inner.ps1) {exit}

### main.ps1
if(. .\inner.ps1) {exit}

Write-Host "second";
Write-Host -NoNewLine "Press any key to continue...";
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");

当然,在运行$recurseScript之前,定义的变量$TRUEmain.ps1不应该存在。