在Invoke-Command Cmdlet中处理ScriptBlock中的错误

22

我正在尝试使用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中正确设置,因为文本:"Error in:" 显示了错误信息。
Invoke-Command的ErrorVariable没有被设置(这是我所期望的)。
我的问题是:
是否有可能将Invoke-Command的ErrorVariable设置为我在ScriptBlock中得到的错误信息?
我知道我也可以使用InstalUtil、WMI和SC来安装服务,但目前这不相关。
4个回答

24
不,您无法使Invoke-Command调用中的Errorvariable与脚本块中相同。但是,如果您的目标是“检测和处理脚本块中的错误,并将错误返回到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 现在包含错误信息。


18
@latkin,在这个回答中,您应该指出这种方法能够奏效的原因是将通常不会终止程序的错误转化为一种可以捕获的终止程序的错误,以便您可以使用try/catch语句进行处理。建议使用值Stop来代替数字1作为-ErrorAction参数,这样更加清晰易懂。 - Keith Hill

15

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

总的来说,我认为将错误信息保留在错误流中,与正常输出分开是更好的选择。


我使用ErrorVariable来检查是否发生了错误。执行命令后,我会检查它,如果不为空,就将其写入文件,然后取消整个脚本的执行。 - flayn
但是你仍然想将错误信息返回给调用者,对吗?如果是这样,你可以在脚本块的末尾输出错误记录。 - Keith Hill
是的,但我不需要可视化信息。我需要一些逻辑,以便我可以告诉命令已经失败,这样我就可以中止脚本。 - flayn
3
在执行Invoke-Command后,请检查$?。 这是确定命令是否失败或成功的典型方式。 - Keith Hill
我尝试了$?的方法,但在我的情况下并没有起作用。我在Invoke-Command中调用了subinacl.exe,并在错误的帐户名称上运行,但之后$?仍然返回True。对于exe调用,我想传回退出代码是必要的。 - Matthew MacFarland

5

这几乎肯定不是“正确”的答案,但当我希望在脚本中使用 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] }

1
只要它能正常工作,就不要质疑它。这是我需要的简单方法,谢谢。 - Dave

0

从严格意义上讲,我认为答案是否定的——无法将 Invoke-Command 的 ErrorVariable 设置为脚本块内 ErrorVariable 的内容。ErrorVariable 只用于附加到的命令。

然而,你可以将脚本块中的变量传递到 Invoke-Command 的作用域中。在你的代码中,你使用 -ErrorVariable errortext 运行了 New-Service 命令。相反,通过在变量名之前添加“script:”的前缀来在 'script' 作用域中创建变量,例如: -ErrorVariable script:errortext。这样就可以在脚本块内外都使用该变量。

现在,你的最后一行Write-Host("Error out: " + $errortext)将会输出脚本块内生成的错误信息。

更多信息请参见此处此处


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接