1

If I do a simple test of exit codes directly with single commands:

[user@localhost ~]$ date
Tue Sep  8 14:00:11 CEST 2020
[user@localhost ~]$ echo $?
0
[user@localhost ~]$ dat
bash: dat: command not found...
[user@localhost ~]$ echo $?
127

But if I want to get them into an if sentence, I see that for the exit code 0 the output is OK, however, for the "command not found" code (it should be 127) it returns "1".

[user@localhost ~]$ date 
Tue Sep  8 14:02:18 CEST 2020
[user@localhost ~]$ if [ $? -eq 0 ]; then   echo "Success: $?"; else   echo "Failure: $?" >&2; fi
Success: 0
[user@localhost ~]$ dat
bash: dat: command not found...
[user@localhost ~]$ if [ $? -eq 0 ]; then   echo "Success: $?"; else   echo "Failure: $?" >&2; fi
Failure: 1

What am I missing?

Zumo de Vidrio
  • 2,021
  • 2
  • 15
  • 33
  • 6
    The `[` command modifies `$?` too. Use: `dat; status=$?; if [ $status = 0 ]; then echo "Success: $status"; else echo "Failure: $status"; fi`. – Jonathan Leffler Sep 08 '20 at 12:11
  • 2
    The `$?` in the `else` clause is the exit status of `[ $? -eq 0 ]` in the `if` clause. – M. Nejat Aydin Sep 08 '20 at 12:14
  • @ZumodeVidrio : Looks like a [XY-problem](http://xyproblem.info/) to me. What your use case? What do you want to achieve? – user1934428 Sep 08 '20 at 13:19
  • @user1934428 No no, it's not an XY-problem at all, I was just trying to add some debug into a script and wanted to take into account the error code in case it happens. – Zumo de Vidrio Sep 08 '20 at 14:14
  • Then, why only 0 and 127? Codes larger 127 are virtually always errors occuring when starting a process, and most programs use codes between 1 (or 2) and 126 for errors too. And why do you want to continue on failure? Wouldn't a `set -e` better fulfil what you want? – user1934428 Sep 08 '20 at 14:38
  • @user1934428 127 was just the example I used for posting the question, in my script the command (which performs a backup) will never give 127, of course. I need to continue the script because I perform something like: stop services - command execution - start services. Then even if the command fails I will need to start the services. – Zumo de Vidrio Sep 08 '20 at 14:44
  • Doesn't it depend on the command in your script? If most commands in your script should not return a non-zero exit code, you could use `set -e` and explicitly give permission to misbehave to those few commands you want to exempt. Just an idea..... – user1934428 Sep 08 '20 at 15:30

2 Answers2

7

If you want to catch and print the exit code for each command, I would not bother saving it in a variable. This would be necessary only if you need this exit code agains at a later time, or if there are some non-zero exit codes which you don't regard as error.

Instead, I would do it like this:

Replace your existing

command_to_verify

by

if command_to_verify
then
  echo Success
else
  echo Failure: $?
fi
user1934428
  • 19,864
  • 7
  • 42
  • 87
  • See also [Why is testing “$?” to see if a command succeeded or not, an anti-pattern?](https://stackoverflow.com/questions/36313216/why-is-testing-to-see-if-a-command-succeeded-or-not-an-anti-pattern) – tripleee Feb 01 '21 at 06:12
  • I had code very similar to this that was not working that led me to this question. `if ! command_to_verify; then echo Failure: $?; fi` was not working. – Thick_propheT Jun 21 '22 at 21:17
  • 1
    If you write, `! command_to_verify`, the `!` **changes** `$?`, and this is the reason why you loose the actual status value of your command. – user1934428 Jun 22 '22 at 05:51
1

Every command has it own exit state, so the command [ 1 will override $?. To prevent this, save the exit code to a variable, and use that in the if statement;

le=$?
if [ "$le" -eq 0 ]; then   echo "Success: $le"; else   echo "Failure: $le" >&2; fi

Try it online!


1: The single bracket [ is actually an alias for the test command, it's not syntax. For more info, check this comment on unix.stackexchange

0stone0
  • 34,288
  • 4
  • 39
  • 64