1

My script calls an external command (e.g. readlink) which will either:

  • write a useful string to its stdout on success, or…
  • return a non-zero exit status on failure

I know how to do each individually, e.g. to capture the stdout I can use

MY_VAR=$(readlink /)

Or to check the result I could use:

if readlink /tmp; then
   echo "Success"
fi

But is there a clean/clear way to do both ± simultaneously? I'm not sure the following would work:

if MY_VAL=`readlink "$MY_ARG"`; then
    echo "Value is ${MY_VAL}"
else
    echo "Not found"
fi

UPDATE: so I tried that, and it did work. But is it only a coincidence, e.g. happens to work because readlink's output is empty at the same time it errors?

As a particular constraint, I need to do this in an environment where set -e is specified, i.e. if other commands return an error the overall script will fail. Generic shell preferred, unless bash has a better mechanism.

natevw
  • 16,807
  • 8
  • 66
  • 90

3 Answers3

4

Just:

if MY_VAR=$(readlink /); then
   echo "Success"
fi

Command substitution return status is the return status of the last command executed (or the return status of the subshell executed inside). So $(true; false;) returns nonzero status. The return status of a line with command substitution is equal to the last executed command. So:

 a=$(true)$(false)

will always return nonzero return status. But:

 a=$(false)$(true)

will return a zero return status, because the last executed command substitution command is true which returns a zero status.

It's normal in scripts to do if var=$(...); then which allows for checking the return status of a command while saving it's standard output.

Don't use backticks `. They are the same as $( .. ) but are less readable, can't be nested and are deprecated.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    Very helpful, thanks for expanding on your original confirmation! Appreciate the style feedback on the backticks too. – natevw Nov 30 '18 at 20:41
  • 1
    Caveat: if you use something like `if local var=$(somecommand)`, then you're testing the status of the `local` command, not of `somecommand`. The same goes for `export`, `declare`, etc. See [this question](https://stackoverflow.com/questions/33372559/exit-status-wrong-with-local-variable-assignment). – Gordon Davisson Nov 30 '18 at 22:44
0

You can check the exit status of your last command executed with $? In shell and see if it holds 0 for success or 1 for failure

Mehdi Bahra
  • 319
  • 1
  • 8
  • Unfortunately, this will not work if `set -e` is active, since a failed command execution will cause my own script to automatically fail before I am able to check the `$?` myself. – natevw Nov 30 '18 at 20:59
  • 1
    @natevw Don't use `set -e`; there are a *lot* of corner cases where it won't work like you expect. It's better to do your own error handling. – chepner Nov 30 '18 at 21:07
0

So my "wish it worked like this example" is correct!

if MY_VAL=$(readlink "$MY_ARG"); then
    echo "Value is ${MY_VAL}"
else
    echo "Not found"
fi

The reason is that the "exit code" of a variable assignment like this is the exit code of the command inside. The variable assignment happens and the exit status is propagated, on both success and failure.

natevw
  • 16,807
  • 8
  • 66
  • 90