CMD &&/|| behavior unexpected after CMD script with "exit /b X" - cmd

CMD && and || operators work as expected when I use node.js to set ERRORLEVEL:
Microsoft Windows [Version 10.0.19044.1645]
(c) Microsoft Corporation. All rights reserved.
C:\Users\Blaine>node -e "process.exit(0)"
C:\Users\Blaine>echo %ERRORLEVEL%
0
C:\Users\Blaine>node -e "process.exit(1)"
C:\Users\Blaine>echo %ERRORLEVEL%
1
C:\Users\Blaine>node -e "process.exit(2)"
C:\Users\Blaine>echo %ERRORLEVEL%
2
C:\Users\Blaine>node -e "process.exit(0)" && echo expected
expected
C:\Users\Blaine>node -e "process.exit(1)" || echo expected
expected
C:\Users\Blaine>node -e "process.exit(2)" || echo expected
expected
C:\Users\Blaine>node -e "process.exit(0)" || echo UNexpected
C:\Users\Blaine>node -e "process.exit(1)" && echo UNexpected
C:\Users\Blaine>node -e "process.exit(2)" && echo UNexpected
C:\Users\Blaine>
But behavior is different and unexpected when I use CMD scripts 'exit0.cmd', 'exit1.cmd, 'exit2.cmd' which are:
#echo off
exit /b 0
and
#echo off
exit /b 1
and
#echo off
exit /b 2
ERRORLEVEL gets set exactly the same as for the node.js executions:
C:\Users\Blaine\tmp>exit0
C:\Users\Blaine\tmp>echo %ERRORLEVEL%
0
C:\Users\Blaine\tmp>exit1
C:\Users\Blaine\tmp>echo %ERRORLEVEL%
1
C:\Users\Blaine\tmp>exit2
C:\Users\Blaine\tmp>echo %ERRORLEVEL%
2
but && and || don't work right for non-0 ERRORLEVELS:
C:\Users\Blaine\tmp>exit0 && echo expected
expected
C:\Users\Blaine\tmp>exit0 || echo UNexpected
C:\Users\Blaine\tmp>exit1 && echo UNexpected
UNexpected
C:\Users\Blaine\tmp>exit1 || echo expected
C:\Users\Blaine\tmp>exit2 && echo UNexpected
UNexpected
C:\Users\Blaine\tmp>exit2 || echo expected
C:\Users\Blaine\tmp>
What the hell is going on?

Related

Posix Shell test non zero exit code script termination when set -e

Reading the manual of the test shell command:
man test
Description states:
Exit with the status determined by EXPRESSION.
But this conflicts with my POSIX sh test script examples, where I use set -eu that should terminate a script if a command's exit status is not zero:
#!/bin/sh
set -eu
status=1
test "$status" -ne 0 && echo "Status not eq 0"
echo "(A) Exit code for "$status" = $?"
echo ""
status=0
test "$status" -ne 0 && echo "Status not eq 0"
echo "(B) Exit code for "$status" = $?"
echo ""
status=1
test "$status" -ne 0
echo "(C) Exit code for "$status" = $?"
echo ""
status=0
test "$status" -ne 0
echo "(D) Exit code for "$status" = $?"
running that script provides the output:
Status not eq 0
(A) Exit code for 1 = 0
(B) Exit code for 0 = 1
(C) Exit code for 1 = 0
This is how I understand flow of a execution for:
status=1
test "$status" -ne 0 && echo "Status not eq 0"
echo "(A) Exit code for "$status" = $?"
with output:
Status not eq 0
(A) Exit code for 1 = 0
test "$status" -ne 0 evaluates to true so its exit code is zero therefore an expression after boolean && is executed and since it is just an echo it returns also zero exit code so the script does not brake and echoes next line (A) Exit code for 1 = 0
but for the
status=0
test "$status" -ne 0 && echo "Status not eq 0"
echo "(B) Exit code for "$status" = $?"
with the output:
(B) Exit code for 0 = 1
Following previous reasoning the test should return non zero therefore an expression after && should not be executed (and it is not) but the script executes even the test returned non zero exit code (B) Exit code for 0 = 1.
Why does the script continue execution? It should brake due to non zero exit status.
There is no output for the case:
status=0
test "$status" -ne 0
echo "(D) Exit code for "$status" = $?"
that suggests to me the script execution was terminated at the line test "$status" -ne 0 and if you run echo $? after running the script you'll get in fact 1.
But how come the script is terminated when the test returns non zero exit status for the example (D) but it is not for the example (B)?
The only difference between (D) and (B) is that (B) has && echo "Status not eq 0" after the test but that is not executed and the exit status is 1 so in case of (B) the script should be terminated but it wasn't and if an exit status of a test is treated somehow so specially so it does not terminate a script having a set -e then why it terminates script for the (D) example?
EDIT
Similarly to test behaves ls
for the script:
#!/bin/sh
set -eu
ls notexitingfile && echo "File exists"
echo "Exit code for ls = $?"
ls notexitingfile
echo "Exit code for ls = $?"
the output is:
ls: cannot access 'notexitingfile': No such file or directory
Exit code for ls = 2
ls: cannot access 'notexitingfile': No such file or directory
note Exit code for ls = 2 for the first example and lack of it for the second.
I think the cause of the unexpected behavior may be my misunderstanding of the script termination (set -e) due to non zero exit code when using && operator.
The set -e option applies only to a single command.
It does not apply for commands combined with || or &&.
man bash says:
The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !.

Echo message based on condition failure bash

Is there a simple way to echo a message for a failure in a multi conditional if statement rather than keeping track of a flag or something.
Example is
if [ $i != 0 ] && (cat file.txt | grep -q sometext)
then
echo allGood
exit 0
else
echo either i is not 0 or file.txt doesnt contain sometext
exit -1
fi
I want to be more concise on which of the conditionals failed while remaining efficient if possible. So instead of the echo being one or other failed, in the if statement itself if first one fails I echo corresponding failure and then I just exit with exit status
Try something like
[ $i != 0 ] || echo i is zero && exit -1
grep -q sometext file.txt || echo sometext not found && exit -1
echo all good && exit 0
(note that I've also removed your UUOC)

How to examine return value of wget in windows

How do I convert the below code into CMD line code for windows?
r=`wget -q www.someurl.com`
if [ $r -ne 0 ]
then echo "Not there"
else echo "OK"
fi
something like this:
wget.exe -q www.someurl.com
if errorlevel 1 (
echo not there
) ELSE (
echo ok
)
the error can be printed with
echo Failure is %errorlevel%
Using cmd's short-circuit && and || operators:
wget.exe -q www.someurl.com && (
echo OK
) || (
echo Not there
)
If the statements are short, you can make this into a one-liner
wget.exe -q www.someurl.com && ( echo OK ) || ( echo Not there )
This method only notes if the exit code is zero or nonzero. If you need the actual exit code, use the if statements in Stuart Siegler's answer.

Complement the exit status in if with -e set

Is there a simple way to do something like:
if ! some_command; then
some_commands;
fi
or
if [[ com1 && com2 ]]; then
something;
fi
where it's the exit status of com1 and com2 that are used.
I realize that I can do things like get the exit status even with -e set by using || and check that, etc. Just wondering if there was something simpler that I am missing.
ADDENDUM: I also realize that I could do:
if some_command; then
else
some_commands;
fi
! can be used outside of an if statement; it's the general exit-status inverter, not part of the if syntax.
! some_command && { some_commands; }
and
some_command || { some_commands; }
are equivalent. You can also use
com1 && com2 && { some_commands; }
for first: you can write something like: where <command1/2> is/are some command(s)/script(s) with/without parameter
$ <command1> 2>/dev/null || <command2> 2>/dev/null
for second: you can write something like:
$ ( <command1> && <command2> ) 2>/dev/null && <command3> 2>/dev/null
ex:
$ echo 1 2>/dev/null || echo 0 2>/dev/null
1
$ ech1o 1 2>/dev/null|| echo 0 2>/dev/null
0
AND
$ ( echo 1 && echo 2 ) 2>/dev/null && echo 3
1
2
3
The following won't print anything as echo 3 will never be executed (due to echo1 is not a valid command)
$ ( echo1 1 && echo 2 ) 2>/dev/null && echo 3

What causes the exit status to change for the same command even when the output is identical?

In the below code, I am trying to check if the command within the if condition completed successfully and that the data was pushed into the target file temp.txt.
Consider:
#!/usr/bin/ksh
A=4
B=1
$(tail -n $(( $A - $B )) sample.txt > temp.txt)
echo "1. Exit status:"$?
if [[ $( tail -n $(( $A - $B )) sample.txt > temp.txt ) ]]; then
echo "2. Exit status:"$?
echo "Command completed successfully"
else
echo "3. Exit status:"$?
echo "Command was unsuccessfully"
fi
Output:
$ sh sample.sh
1. Exit status:0
3. Exit status:1
Now I can't get why the exit status changes above.. when the output of both the instances of the tail commands are identical. Where am I going wrong here..?
In the first case, you're getting the exit status of a call to the tail command (the subshell you spawned with $() preserves the last exit status)
In the second case, you're getting the exit status of a call to the [[ ]] Bash built-in. But this is actually testing the output of your tail command, which is a completely different operation. And since that output is empty, the test fails.
Consider :
$ [[ "" ]] # Testing an empty string
$ echo $? # exit status 1, because empty strings are considered equivalent to FALSE
1
$ echo # Empty output
$ echo $? # exit status 0, the echo command executed without errors
0
$ [[ $(echo) ]] # Testing the output of the echo command
$ echo $? # exit status 1, just like the first example.
1
$ echo foo
foo
$ echo $? # No errors here either
0
$ [[ $(echo foo) ]]
$ echo $? # Exit status 0, but this is **NOT** the exit status of the echo command.
0

Resources