Powershell:如何在一行中运行外部命令并检查其成功性?

Powershell:如何在一行中运行外部命令并检查其成功性?,powershell,if-statement,Powershell,If Statement,在bash中,我可以这样做: if this_command >/dev/null 2>&1; then ANSWER="this_command" elif that_command >/dev/null 2>&1; then ANSWER="that_command" else ANSWER="neither command" fi 但在Powershell中,我必须这样做: this_command >/dev/null 2>

在bash中,我可以这样做:

if this_command >/dev/null 2>&1; then
  ANSWER="this_command"
elif that_command >/dev/null 2>&1; then
  ANSWER="that_command"
else
  ANSWER="neither command"
fi
但在Powershell中,我必须这样做:

this_command >/dev/null 2>&1
if ($?) {
  ANSWER="this_command"
} else { 
  that_command >/dev/null 2>&1
  if ($?) {
    ANSWER="that_command"
  } else {
    ANSWER="neither command"
  }
}

或者类似于
($LASTEXITCODE-eq 0)
。如何使Powershell看起来像bash?我不是Powershell专家,但我不能相信它没有提供某种方法来运行命令并在单个语句中检查其返回代码,这种方法可以在if-elseif-else语句中使用。使用必须以这种方式测试的每个外部命令来读取此语句将变得越来越困难。

您不能像在
bash
中那样以内联方式执行此操作,但您可以在一行中使用两条语句,并用分号分隔

MyProgram.exe -param1 -param2 -etc *>$null; if( $LASTEXITCODE -eq 0 ) {
  # Success code here
} else {
  # Fail code here
}
此外,您不能将
$?
与命令一起使用,只能使用Powershell cmdlet,这就是为什么我们检查
$LASTEXITCODE-eq 0
而不是使用
$?


请注意,可以内联评估cmdlet,而不是外部命令。例如:

if( Test-Connection stackoverflow.com ){
  "success"
} else {
  "fail"
}

对于PowerShell cmdlet,您可以执行与在bash中完全相同的操作。您甚至不需要在每个分支中执行单独的任务。只需输出要分配的内容,并在变量中收集整个条件的输出

$ANSWER = if (Do-Something >$null 2>&1) {
    'this_command'
} elseif (Do-Other >$null 2>&1) {
    'that_command'
} else {
    'neither command'
}
对于外部命令,它略有不同,因为PowerShell将评估命令输出,而不是退出代码/状态(输出为空)。但您可以在子表达式中运行该命令并输出状态以获得所需的结果

$ANSWER = if ($(this_command >$null 2>&1; $?)) {
    'this_command'
} elseif ($(that_command >$null 2>&1; $?)) {
    'that_command'
} else {
    'neither command'
}

请注意,您必须使用子表达式(
$(…)
),而不是分组表达式(
(…)
),因为您实际上需要在一行中运行两个命令(运行外部命令,然后运行输出状态),后者不支持。

PowerShell的本机错误处理与外部程序执行的基于退出代码的错误信号处理完全不同,不幸的是,PowerShell中使用外部程序的错误处理非常麻烦,需要对自动
$?
$LASTEXITCODE
变量进行显式检查

PowerShell[核心]

$ANSWER = this_command *>$null && "this_command" || 
          (that_command *>$null && "that_command" || "neither command")
  • v7中引入了对Bash样式
    &
    |
    管道链操作符的支持-请参阅

  • 但这也不允许在
    if
    语句中使用外部程序调用,因为PowerShell将继续对命令的输出进行操作,而不是对其隐含的成功状态/退出代码进行操作;有关更多信息,请参阅

解决方案

$ANSWER = this_command *>$null && "this_command" || 
          (that_command *>$null && "that_command" || "neither command")

PowerShell[Core]7.0+

$ANSWER = this_command *>$null && "this_command" || 
          (that_command *>$null && "that_command" || "neither command")
注:

  • 如果
    此\u命令
    该\u命令
    不存在(找不到),则会发生语句终止错误,即语句整体失败

  • 请注意,需要将第二条链括在
    (…)
    中,以便
    和&“that_command”
    此_命令成功时也不会启动

  • *>$null
    用于通过单个重定向方便地使所有流静音

  • 与基于
    if
    的解决方案不同,此技术将外部程序的输出(非抑制)传递给其他程序


Windows PowerShell和PowerShell核心6.x:

  • 如果外部程序调用不产生输出,或者您主动想要抑制其输出,如您的问题所述:

    • 请参阅中基于<代码>$(…)的技术
  • 如果确实需要外部程序的输出

  • $ANSWER = this_command *>$null && "this_command" || 
              (that_command *>$null && "that_command" || "neither command")
    
辅助设备。虚拟
do
循环允许一个相当“低噪音”的解决方案:

$ANSWER = do {

  this_command   # Note: No output suppression
  if ($?) { "this_command"; break }

  that_command   # Note: No output suppression
  if ($?) { "that_command"; break }

  "neither command"

} while ($false)

另一种方法是,如果为false,则让它输出一个空字符串:

if (echo hi | findstr there) { 'yes' }
if (echo hi | findstr hi) { 'yes' }
yes