2017-01-27 4 views
1

Я следующие два сценария:Powershell: Рекурсивный вызов сценария

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

Даже с дот-источников (.), скрипт не может поручить своему вызывающих к exit, поэтому в текущей настройке вам необходимо некоторых условного в main.ps1, который проверяет необходимость выхода после . .\inner.ps1 вызова.

Учитывая, что вспомогательный скрипт inner.ps1 в настоящее время точка-источников и, следовательно, может изменять окружающую среду вызывающего абонента, main.ps1, один спрашивает, почему рекурсии необходимо в целом.
Остальная часть этого ответа, однако, согласна с предпосылкой вопроса.

ОП сам придумал a solution that encapsulates the recursive-invocation details inside inner.ps1, но проблема заключается в том, что с помощью потока успеха (регулярный выходной поток) для передачи управляющей информации - $TRUE или $FALSE - означает, что if заявление, которое проверяет, что информация (if (. .\inner.ps1) ...) потребляет стандартный выпуск из main1.ps1 и поэтому подавляет. [1]

Другими словами: подход работает только если main.ps1 происходит не производить никакого вывода вообще, или вызывающий бывает не волнует, что main.ps1 выходы.

решения основывать поток управления на кодах выхода (которые PowerShell отражает в автоматической переменной $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. 

} 

и:

### 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, не должна существовать заранее (или, по крайней мере, не иметь правдивого значения), поэтому ее имя - $__inner_already_recursed - был выбран для минимизации этого риска.


[1] Write-Host вызовов не влияет, потому что они печатают непосредственно на консоль, но их выход не может быть ни захваченный, ни послал по трубопроводу.
(с дополнительным усилием, вы можете в 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 как определено и $TRUE не должны существовать перед запуском main.ps1.

+0

Вы должны добавить большой отказ от ответственности, что ваш подход является только опцией, если 'main.ps1' не производит регулярного вывода с удачным потоком или вас не интересует этот вывод. Любой полученный им результат (поток-поток) потребляется оператором 'if'. Единственная причина, по которой вы видите вывод, заключается в том, что вы используете 'Write-Host', но это идет прямо на консоль и не может быть захвачено или перенаправлено. – mklement0

Смежные вопросы