If you have the full shell syntax available to you, then you can use if
, which does not appear as a failure to the shell:
if (eval "$cmd"); then
: # do nothing, it worked
else
printf 'Command %s failed with code %d\n' "$cmd" $?
fi
Note that you can't use the more obvious if ! eval…
syntax, as the negation will hide the exit code. The "do nothing" bit there is there because shell syntax requires a then block with a command in it; you can't omit it or leave it empty.
The important thing is that an if
is not a failure to the shell, even though the evaluated command failed.
The parentheses are to run the command in a sub-shell; this prevents the command from e.g., setting variables that affect your script or doing something like exit 1
to kill your script entirely.
Finally, note how I've used eval
instead of unquoted $cmd
. That's because unquoted command does word splitting in a way you probably don't expect. If you had cmd='rm -Rf "foo * baz"'
, then when you run $cmd
what you're actually running is rm -Rf \"foo * baz\"
— which will expand the *
to all files in the directory, deleting everything (in addition to the two files "foo
and baz"
). That's three arguments to rm
, before glob expansion, not the one you expected. eval
will apply the normal shell command parsing, I think leading to fewer surprises. There are other approaches (using arrays, for example) that are safer if this is a command being built up inside your script and not, e.g., a line read from a text file.
cmd
exit how it wants?cmd
. @Jesse_bpipefail
is even available in dashset -e
probably...