1

Take a look at these attempts:

$ case `true` in 0) echo success ;; *) echo fail ;; esac
fail
$ if `true` ; then
> echo "success"
> else 
> echo "fail"
> fi
success

Now, why is the case statement failing? You might wonder why I don't just use the if statement and I shall explain. My command if complex and might return different return codes on which I want act on. I don't want to run the command multiple times and I can't do:

my_command
res = $?
case $? in
...
esac

This is because I use set -e in my script and therefore if my_command returns failure the script aborts.

But I have a workaround...

set +e
my_command
res=$?
set -e
case $? in 
...
esac

But this is ugly, so returning to my initial question... why can I just use the case my_command in ... esac version?

7

You can't use

case $(somecommand) in ...

to test the exit status of somecommand because the command substitution expands to the output of the command, not its exit status.

Using $(true) doesn't work since true doesn't produce any output on standard output.

You could do

{ somecommand; err="$?"; } || true

case $err in
    0) echo success ;;
    *) echo fail
esac

This will stop the script running under errexit (-e) from exiting.

From the bash manual (from the description of set -e):

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 !.

In this case, with only the possibilities to either succeed or to fail, it would be easier to just do

if somecommand; then
    echo success
else
    echo fail
fi
0
6

The syntax

`command`

means to substitute the standard output of the command into the original command line. The output is not the same thing as the exit status. The true command doesn't produce any output, so your command is equivalent to:

case "" in
0) echo success ;;
*) echo fail ;;
esac

You can solve your problem this way:

case `my_command; echo $?` in 
...
esac

BTW, in the if command you don't need the backticks, it should just be:

if true
then echo successs
else echo fail
fi
2

Even with set -e you can still do:

if my_command
then
    echo success
else
    res=$?
    case $res in
      …
    esac
fi

Edit: you could also do

res=0
my_command || res=$?
case $res in
    …
esac
0

The obvious answer: Don't use set -e in this case.

If you really have to, you may need to do something really ugly like:

case "$(bash -c 'my_command; echo "$?"')" in
    0) echo success ;;
    1) echo fail ;;
    2) echo "unexpected failure" ;;
    17) echo "wtf is this" ;;
    *) echo "I never thought this would happen" ;;
esac

But the obvious approach would be:

set +e
my_command
res="$?"
set -e
case "$res" in
    ...

Also note that spaces are not allowed around the equals sign in variable assignment in any shell.

0

I fully agree; disabling -e and re-enabling it is ugly, and should be avoided.

Couple of options

if my_command; then
  echo "my_command ran ok"
else
  rc=$?
  echo "my_command exited with error code $rc"
fi

Or if you want to go the case route (and this is a bit hacky, but it should show capability as opposed to be used).

# PLEASE DO NOT ACTUALLY DO THIS
case "$(my_command > /dev/null && echo 0 || echo $?)" in
  0) echo yeah ;;
  11) echo "bummer" ;;
  *) echo meh ;;
esac

A less ugly solution might be (if you want to use case)

my_command && rc=0 || rc=1
case "$rc" in
...
esac

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.