Reputation: 647
I want to write a PowerShell script to select a JFrog service connection, but the script is not behaving the way I expect it to behave.
When I run the jfrog config use
command manually, it works as expected:
PS C:\Users\rdepew> jfrog config use Dummy
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
PS C:\Users\rdepew> $?
True
I expect to be able to use the $?, which always returns true or false, to detect whether or not jfrog config use
has executed successfully. This snippet illustrates the problem I've got:
PS C:\Users\rdepew> if (jfrog config use Dummy) {
>> Write-Host Dummy exists
>> } else {
>> Write-Host Dummy doesnt exist but I dont believe it
>> }
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
Dummy doesnt exist but I dont believe it
The [Info] line indicates that JFrog is in fact using server connection 'Dummy'. I assume that the $? exit code evaluates as true, just like it did in my manual execution. But my if/else test takes the "if false" branch. Why?
MORE INFO: If I use the name of a nonexistent service connection, I get the expected behavior:
PS C:\Users\rdepew> if (jfrog config use Bogus) {
>> Write-Host Bogus exists
>> } else {
>> Write-Host Bogus doesnt exist
>> }
[Error] Could not find a server with ID 'Bogus'.
Bogus doesnt exist
I don't understand why PS jumps to the else
clause for both True and False cases.
STILL MORE INFO
Thanks to the first answers and comments, I understand a little more. I can rewrite my snippet like this:
jfrog config add Dummy
if ($LASTEXITCODE=0) {
etc.
But I'm still confused about $? behavior. From the command line, $? behaves like this:
PS C:\Users\rdepew> jfrog config use Dummy
[Info] Using server ID 'Dummy' (https://foo.jfrog.io/).
PS C:\Users\rdepew> $?
True
PS C:\Users\rdepew> jfrog config use Bogus
[Error] Could not find a server with ID 'Bogus'.
PS C:\Users\rdepew> $?
False
... which is what one would expect.
Upvotes: 0
Views: 1030
Reputation: 21418
In PowerShell, $?
returns $true
if the prior cmdlet completed without error or $false
if it presented any errors. For external commands, it will return the result of $LASTEXITCODE -eq 0
.
However, when you put a command (external or otherwise) as the condition in an if
statement it doesn't check whether the command succeeded but rather the truthiness of the result. If a command returns a proper $true
or $false
boolean this is straightforward, otherwise, it will convert the output on the success stream to a boolean value. Any excepting the following output evaluates to $true
and, $null
, 0
, empty strings, and empty arrays evaluate to $false
.
What I'm not sure of though is why both commands aren't producing a $true
output at least when run as the if
condition. Another way to write your code would be to run jfrog
outside of the if
and just check the result of $LASTEXITCODE
:
$output = jfrog config use Dummy
if ( $LASTEXITCODE -eq 0 ) {
Write-Host "Dummy exists"
} else {
Write-Host "Dummy doesnt exist but I dont believe it"
}
If other exit codes are acceptable, use this one weird trick to check multiple values without having to chain multiple evals of $LASTEXITCODE
together:
# I use this often when checking the result of MSIEXEC
if ( $LASTEXITCODE -in @(0, 3010) ) {
....
}
Basically, this just says to check that $LASTEXITCODE
exists in the array on the right hand side of -in
. You can use -notin
to negate the evaluation. The -contains/-notcontains
operators are also an option, you just have to reverse the operands from -in/-notin
.
After some additional testing, and vaguely recalling this issue from a few years back at work, I believe jfrog
is writing everything to STDERR which would explain your behavior (you can test with $var = jfrog blah blah blah
and see if $var
contains the output or if it still gets written to console).
If you want to alleviate this, say, in the event you want to capture the output, redirect the Error Stream (basically STDERR) to the Success Stream (basically STDOUT):
$output = jfrog blah blah blah 2>&1
However, I would still rely on checking $LASTEXITCODE
to check for failure rather than the output or $?
. Checking for output results in similar issues presented in this question, and checking $?
requires you to be mindful of updates to your code in the future, since you could very easily slip another command between the execution and result evaluation.
I have a comprehensive answer here on the different output streams and redirection in general which you might find useful.
Upvotes: 2