Обработка ошибок в ScriptBlock в командлете Invoke-Command

Я пытаюсь установить службу на удаленном компьютере с помощью PowerShell.

Пока у меня есть следующее:

Invoke-Command -ComputerName  $remoteComputerName -ScriptBlock {
         param($password=$password,$username=$username) 
         $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
         $credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
         New-Service -Name "XXX" -BinaryPathName "c:\XXX.exe" -DisplayName "XXX XXX XXX" -Description "XXXXXX." -Credential $credentials -ErrorVariable errortext 
         Write-Host("Error in: " + $errortext)
        } -ArgumentList $password,$username -ErrorVariable errortext 


Write-Host("Error out: " + $errortext)

Когда возникает ошибка при выполнении New-Service, переменная $ errortext ErrorVariable устанавливается правильно внутри ScriptBlock, потому что текст: «Ошибка в: показывает мне ошибку.

ErrorVariable Invoke-Command не устанавливается (чего я ожидал).

У меня вопрос:

Можно ли каким-то образом установить для ErrorVariable Invoke-Command значение ошибки, которую я получил внутри ScriptBlock?

Я знаю, что могу также использовать InstalUtil, WMI и SC для установки службы, но на данный момент это не актуально.


person flayn    schedule 26.09.2012    source источник


Ответы (4)


Нет, вы не можете установить Errorvariable из вызова Invoke-Command так же, как в блоке сценария.

Но если ваша цель - «обнаруживать и обрабатывать ошибки в блоке сценариев, а также возвращать ошибки в контекст Invoke-Command вызывающего», тогда просто сделайте это вручную:

$results = Invoke-Command -ComputerName server.contoso.com -ScriptBlock {
   try
   {
       New-Service -ErrorAction 1
   }
   catch
   {
       <log to file, do cleanup, etc>
       return $_
   }
   <do stuff that should only execute when there are no failures>
}

$results теперь содержит информацию об ошибке.

person latkin    schedule 26.09.2012
comment
@latkin, вы должны указать в этом ответе, что причина, по которой это работает, заключается в том, что вы принимаете обычно непрекращающуюся ошибку и конвертируете ее в завершающую ошибку, чтобы вы могли отловить ошибку с помощью оператора try / catch. Было бы проще использовать значение Stop для -ErrorAction вместо 1. - person Keith Hill; 02.10.2012

Список аргументов Invoke-Command является односторонним. Вы можете либо вывести переменную ошибки в скрипт, например, в последней строке блока сценария поставьте:

$errortext

или еще лучше, просто не фиксируйте ошибку через -ErrorVariable вообще. Выходные данные блока сценария, включая ошибки, будут передаваться вызывающей стороне даже через удаленное соединение.

C:\> Invoke-Command -cn localhost { Get-Process xyzzy } -ErrorVariable errmsg 2>$null
C:\> $errmsg
Cannot find a process with the name "xyzzy". Verify the process name and call the cmdlet again.
    + CategoryInfo          : ObjectNotFound: (xyzzy:String) [Get-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
    + PSComputerName        : localhost

В общем, я думаю, что гораздо лучше хранить ошибки в потоке ошибок отдельно от обычного вывода.

person Keith Hill    schedule 26.09.2012
comment
Я использую ErrorVariable, чтобы проверить, не произошла ли ошибка. После выполнения команды я проверяю ее и, если она не пустая, записываю ее в файл, а затем отменяю выполнение всего скрипта. - person flayn; 26.09.2012
comment
Но вы все равно хотите вернуть информацию об ошибке вызывающему абоненту, верно? Если это так, вы можете вывести запись об ошибке в конце блока сценария. - person Keith Hill; 26.09.2012
comment
Да, но визуальная информация мне не нужна. Мне нужна какая-то логика, чтобы я мог сказать, что команда не удалась, чтобы я мог прервать скрипт. - person flayn; 26.09.2012
comment
В этом случае проверьте $? сразу после выполнения Invoke-Command. Это типичный способ определить, завершилась ли команда неудачно или успешно. - person Keith Hill; 27.09.2012
comment
Я пробовал $? техника, и она не сработала в моем случае. Я вызвал subinacl.exe из-за неправильного имени учетной записи внутри Invoke-Command, но потом $? все равно вернул True. С вызовами exe, я думаю, необходимо передать коды выхода обратно. - person Matthew MacFarland; 25.04.2018

Это почти наверняка не «правильный» ответ, но именно его я использую, когда хочу, чтобы Invoke-Command выдал ошибку в скрипте.

$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { throw $error[0] }

Если вы хотите сохранить ошибку в переменной, вы можете сделать следующее:

$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { $myErrorVariable = $error[0] }
person Adam S    schedule 17.04.2019
comment
Не стучите, если это сработает, это было как раз то, что мне было нужно, спасибо - person Dave; 23.03.2021

В самом строгом смысле, я считаю, что ответ отрицательный, вы не можете установить для параметра ErrorVariable Invoke-Command значение ErrorVariable внутри блока скрипта. ErrorVariable предназначена только для команды, к которой она привязана.

Однако вы можете передать переменную в блоке сценария в область действия Invoke-Command. В вашем коде вы запускаете команду New-Service с -ErrorVariable errortext. Вместо этого создайте свою переменную в области 'script', поставив перед именем переменной "script:", например: -ErrorVariable script:errortext. Это делает переменную доступной как внутри блока скрипта, так и вне его.

Теперь ваша последняя строка Write-Host("Error out: " + $errortext) выведет ошибку, сгенерированную внутри блока скрипта.

Дополнительная информация здесь и здесь.

person Todd Walton    schedule 27.06.2017