ТЛ; др
Поскольку ваша Invoke-SilentlyAndReturnExitCode
функция определена в модуле , вы должны воссоздать свой блок скрипта в области действия этого модуля , чтобы он увидел локальный модуль $ErrorActionPreference
значение Continue
:
# Use an in-memory module to demonstrate the behavior.
$null = New-Module {
Function Invoke-SilentlyAndReturnExitCode {
param([scriptblock] $Command, $Folder)
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try
{
Write-Host $ErrorActionPreference # local value
# *Recreate the script block in the scope of this module*,
# which makes it see the module's variables.
$Command = [scriptblock]::Create($Command.ToString())
# Invoke the recreated script block, suppressing all output.
& $Command *>$null
# Output the exit code.
$LASTEXITCODE
}
catch
{
-1
}
finally
{
Pop-Location
}
}
}
$ErrorActionPreference = 'Stop'
$Command = { Out-Host -InputObject $ErrorActionPreference; cmd /c dir xo-xo-xo }
Invoke-SilentlyAndReturnExitCode $Command
В Windows вышеприведенное теперь печатает следующее, как и ожидалось:
Continue
Continue
1
То есть воссозданный блок сценария $Command
видел локальное значение $ErrorActionPreference
функции, а блок catch
был не сработал.
Предостережение :
Это будет работать только в том случае, если блок сценария $Command
не содержит ссылок на переменные в исходной области , кроме переменных в global scope.
альтернатива - обойти это ограничение - определить функцию вне модуля (при условии, что вы также вызывая его из кода, который находится вне модулей).
Справочная информация
Поведение подразумевает, что ваша Invoke-SilentlyAndReturnExitCode
функция определена в модуле , и каждый модуль имеет свой собственный домен областей (иерархию областей).
Ваш $Command
блок сценария, поскольку он был определен вне этого модуля, привязан к области действия default и даже при выполнении изнутри модуля он продолжает видеть переменные из области действия , в которой она была определена .
Следовательно, $Command
по-прежнему видит значение Stop
$ErrorActionPreference
, хотя для кода, создаваемого модулем внутри функции, это будет Continue
, из-за установки локальной копии $ErrorActionPreference
внутри функции модуля.
Возможно, что удивительно, все еще $ErrorActionPreference
в действии внутри $Command
управляет поведением, а не значением локальной функции.
С таким перенаправлением, как 2>$null
для *>$null
, в то время как Stop
является эффективным значением $ErrorActionPreference
, простое присутствие вывода stderr из внешней программы - указывает ли это на истинную ошибку не - запускает завершающая ошибка и, следовательно, ветвь catch
.
Это определенное поведение - когда явное намерение подавить вывод stderr вызывает ошибку - следует рассматривать как ошибку , и в сообщается об этой проблеме GitHub .
Общее поведение , однако - блок сценария, выполняющийся в области действия, в которой он был определен, - хотя и неочевидный, - по проекту .
Примечание : Остальная часть этого ответа является его первоначальной формой, которая содержит общую справочную информацию, которая, однако, не охватывает аспект модуля, рассмотренный выше.
*> $null
можно использовать для отключения всех выходных данных команды - нет необходимости отдельно подавлять поток вывода успеха (>
, подразумевается 1>
) и поток вывода ошибок (2>
).
Как правило, $ErrorActionPreference
не влияет на вывод ошибок из внешних программ (например, git
), поскольку stderr вывод из внешних программ обход Поток ошибок PowerShell по умолчанию.
Однако есть исключение: установка $ErrorActionPreference
в 'Stop'
фактически делает перенаправления, такие как 2>&1
и *>$null
, выдачей завершающей ошибки, если внешняя программа, такая как git
, производит какой-либо вывод stderr .
Это неожиданное поведение обсуждается в этой проблеме GitHub .
В противном случае вызов внешней программы никогда не вызовет завершающую ошибку, которую обработает оператор try
/ catch
. Успех или неудача могут быть выведены только из автоматической переменной $LASTEXITCODE
.
Поэтому напишите вашу функцию следующим образом , если вы определяете (и вызываете) ее вне модуля :
function Invoke-SilentlyAndReturnExitCode {
param([scriptblock]$Command, $Folder)
# Set a local copy of $ErrorActionPreference,
# which will go out of scope on exiting this function.
# For *> $null to effectively suppress stderr output from
# external programs *without triggering a terminating error*
# any value other than 'Stop' will do.
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try {
# Invoke the script block and suppress all of its output.
# Note that if the script block calls an *external program*, the
# catch handler will never get triggered - unless the external program
# cannot be found.
& $Command *> $null
$LASTEXITCODE
}
catch {
# Output the exit code used by POSIX-like shells such
# as Bash to signal that an executable could not be found.
127
} finally {
Pop-Location
}
}