Donal M
Donal M

Reputation: 1315

Use of $? in an if statement conditional expression

Take the following bash script:

#!/bin/bash

ls /sdfg

if [ $? -gt 0 ]
then
        echo "Command failed: $?"
else
        echo "Command succeeded: $?"
fi

The output will be:

ls: cannot access /sdfg: No such file or directory
Command failed: 0

The failed ls command actually returns 2, the if statement catches this, and echos command failed, but why does $? get updated to zero? It looks like the if/then lines are being treated as a new command.

Upvotes: 1

Views: 1648

Answers (3)

Charles Duffy
Charles Duffy

Reputation: 295619

Collect the return value immediately after the command being tested if you're going to reuse it:

#!/bin/bash

# best-practice: collect *on the same line*, so any logging added in the future doesn't
# ...change your exit status.
ls /sdfg; retval=$?

if [ $retval -gt 0 ]; then
        echo "Command failed: $retval" # 
else
        echo "Command succeeded: $retval"
fi

However, the best practice is to directly operate on the command's exit status without needing to refer to $? at all:

if ls /sdfg; then
  echo "Command succeeded: $?"
else
  echo "Command failed: $?"
fi

Upvotes: 1

tripleee
tripleee

Reputation: 189628

You should rarely need to examine $? explicitly anyway, since doing that is the purpose of if (and other conditional builtins like while). The proper way to write that is

if ls /sdfg; then
    echo "Command succeeded: $?"
else
    echo "Command failed: $?"
fi

Notice how the failure case is now the else branch. (If you don't particularly care about preserving the failure exit code, you can use if ! ls... and keep the success branch in the else, but your question seems to be specifically about preserving it, which negation with ! will not do.)

Upvotes: 2

Marc B
Marc B

Reputation: 360762

[ is actually a binary, not just some punctuation:

$ ls -l /usr/bin/[
-rwxr-xr-x 1 root root 51920 Feb 18 07:37 /usr/bin/[
$ /usr/bin/[ --version
[ (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.

and on some systems may be a symlink to test.

So here

if [ $? -gt 0 ]

$? is the result of your ls call, but here

    echo "Command failed: $?"

$? is the result of having executed [ on the previous line.

If you want to test $? from a command multiple times, you'll have to stuff it into a temp var:

ls blahblah
temp=$?
if [ $temp ... ]
if [ $temp ... ]

Upvotes: 2

Related Questions