DrStrangepork
DrStrangepork

Reputation: 3144

Powershell: How to run an external command and checks its success in a single line?

In bash, I can do this:

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

but in Powershell, I have to do this:

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"
  }
}

or something similar with ($LASTEXITCODE -eq 0). How do I make the Powershell look like bash? I'm not a Powershell expert, but I cannot believe that it doesn't not provide some means of running a command and checking its return code in a single statement in a way that could be used in an if-elseif-else statement. This statement would be increasingly difficult to read with every external command that must be tested in this way.

Upvotes: 3

Views: 880

Answers (4)

mklement0
mklement0

Reputation: 440297

PowerShell's native error handling works completely differently from the exit-code-based error signaling performed by external programs, and, unfortunately, error handling with external programs in PowerShell is cumbersome, requiring explicit checks of the automatic $? or $LASTEXITCODE variables.

PowerShell [Core]:

  • introduced support for Bash-style && and || pipeline-chain operators in v7 - see this answer.

  • but this will not also enable use of external-program calls in if statements, because there PowerShell will continue to operate on the output from commands, not on their implied success status / exit code; see this answer for more information.

Solutions:


PowerShell [Core] 7.0+:

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

Note:

  • If this_command or that_command don't exist (can't be found), a statement-terminating error occurs, i.e. the statement fails as a whole.

  • Note the need to enclose the 2nd chain in (...) so that && "that_command" doesn't also kick in when this_command succeeds.

  • *>$null is used to conveniently silence all streams with a single redirection.

  • Unlike an if-based solution, this technique passes (non-suppressed) output from the external programs through.


Windows PowerShell and PowerShell Core 6.x:

  • If the external-program calls produce no output or you actively want to suppress their output, as in your question:

  • If you do want the external programs' output:

An aux. dummy do loop allows for a fairly "low-noise" solution:

$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)

Upvotes: 1

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200503

For PowerShell cmdlets you can do the exact same thing you do in bash. You don't even need to do individual assignments in each branch. Just output what you want to assign and collect the output of the entire conditional in a variable.

$ANSWER = if (Do-Something >$null 2>&1) {
    'this_command'
} elseif (Do-Other >$null 2>&1) {
    'that_command'
} else {
    'neither command'
}

For external commands it's slightly different, because PowerShell would evaluate the command output, not the exit code/status (with empty output evaluating to "false"). But you can run the command in a subexpression and output the status to get the desired result.

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

Note that you must use a subexpression ($(...)), not a grouping expression ((...)), because you effectively need to run 2 commands in a row (run external command, then output status), which the latter doesn't support.

Upvotes: 5

js2010
js2010

Reputation: 27606

Another approach is to have it output an empty string if it's false:

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

Upvotes: 1

codewario
codewario

Reputation: 21488

You can't do it inline like in bash, but you can one-line this with two statements on one line, separated by a semi-colon ;:

MyProgram.exe -param1 -param2 -etc *>$null; if( $LASTEXITCODE -eq 0 ) {
  # Success code here
} else {
  # Fail code here
}

Also, you can't use $? with commands, only Powershell cmdlets, which is why we check that $LASTEXITCODE -eq 0 instead of using $?.


Note that you CAN evaluate cmdlets inline, just not external commands. For example:

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

Upvotes: 1

Related Questions