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

Powershell 在Invoke Command Cmdlet中处理ScriptBlock中的错误,powershell,error-handling,invoke-command,Powershell,Error Handling,Invoke Command,我正在尝试使用powershell在远程计算机上安装服务 到目前为止,我有以下几点: Invoke-Command -ComputerName $remoteComputerName -ScriptBlock { param($password=$password,$username=$username) $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force $

我正在尝试使用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)
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { $myErrorVariable = $error[0] }
当执行新服务时出现错误时,$errortext ErrorVariable将在ScriptBlock内正确设置,因为文本:“error in:显示错误

Invoke命令的ErrorVariable未设置(这是我所期望的)

我的问题是:

是否可以将Invoke命令的ErrorVariable设置为我在ScriptBlock中得到的错误?


我知道我也可以使用InstalUtil、WMI和SC来安装该服务,但目前这并不相关。

调用命令参数列表是一个单向处理。您可以在脚本中输出错误变量,例如在scriptblock put的最后一行:

$errortext
或者更好的是,根本不要通过-error变量捕获错误。scriptblock输出(包括错误)将返回到调用方,即使是通过远程连接

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

一般来说,我认为最好将错误保留在错误流上,与正常输出分开。

不,您无法从
调用命令
调用中获得与脚本块中相同的
错误变量

但如果您的目标是“检测并处理scriptblock中的错误,并将错误返回到
Invoke命令
caller”的上下文中,则只需手动执行即可:

$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=Invoke命令-ComputerName server.contoso.com-ScriptBlock{
尝试
{
新服务-错误操作1
}
抓住
{
返回$_
}
}

$results
现在包含错误信息。

严格来说,我认为答案是否定的,您不能将Invoke命令的ErrorVariable设置为脚本块内ErrorVariable的内容。ErrorVariable仅用于它所附加的命令

但是,您可以将脚本块中的变量传递出去以调用命令的作用域。在代码中,您可以使用
-ErrorVariable errortext
运行新的服务命令。相反,请在“script”作用域中创建变量,方法是在变量名称前面加上“script:,如下所示:
-ErrorVariable脚本:errortext
。这使得变量在脚本块外部和内部都可用

现在,您的最后一行
Write Host(“Error out:+$errortext)
将输出脚本块内部生成的错误


更多信息和。

这几乎肯定不是“正确”的答案,但当我希望调用命令在脚本中抛出错误时,我会使用这一答案

$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { throw $error[0] }
如果要将错误保留在变量中,可以执行以下操作:

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)
$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { $myErrorVariable = $error[0] }

我使用error变量检查是否发生了错误。在执行一个命令后,我检查它,如果它不是空的,我将它写入一个文件,然后取消整个脚本的执行。但是您仍然希望将错误信息返回给调用方,对吗?如果是这样,您可以在脚本块的末尾输出错误记录。是的,但我不需要可视化信息。我需要一些逻辑来判断命令失败,这样我就可以中止脚本。在这种情况下,请在执行Invoke命令后立即检查
$?
。这是确定命令是否失败或成功的典型方法?技术和它没有工作在我的情况下。我在Invoke命令中以错误的帐户名调用了subinacl.exe,但之后是$?结果仍然是真的。对于exe调用,我想返回退出代码是必要的。@latkin,您应该在回答中指出,这种方法之所以有效,是因为您正在处理一个通常不终止的错误,并将其转换为终止错误,这样您就可以用try/catch语句捕获该错误。使用值
Stop
for-ErrorAction而不是1会更清楚。如果它有效,请不要敲它,这只是我需要的简单的东西,谢谢