1

I want to run a potentially invalid command in a variable cmd. I can't afford to have it just fail me and retrieve the exit status. What I can try to do is pipe it to : and then I'll have a successful exit status.

$ $cmd | :
$ echo $?
0

Now, I want to know the exit status of cmd without running it by itself. In Bash I would be able to accomplish this using PIPESTATUS. But I'm using dash.

I tried to run bash -c "$cmd | : ; rc=$(echo ${PIPESTATUS[@]} | head -c1)" but I get a "Bad substituion error".

My questions are:

  1. How do accomplish my goal of returning the exit status of the first command in dash?
  2. What is it about what I'm doing that yields an error in dash?
  • Why do you need this at all? Just let cmd exit how it wants? – Jesse_b May 16 at 13:30
  • Because then, things that are outside of my control won't work. I can feed a file with commands to be ran and for some reason if any of the commands fails, the script won't work in some sense. (What sense this is, I can't even tell because I don't have enough visibility/access over what's happening). So I need to make sure that each line is successful. As part of my needs, I also require the exit status of what I referenced above as cmd. @Jesse_b – Wraper May 16 at 13:38
  • I don't understand. Your script should not exit from a single command failure. I don't think pipefail is even available in dash – Jesse_b May 16 at 13:58
  • 1
    @Jesse_b set -e probably... – Stephen Kitt May 16 at 14:22
  • @Jesse_b The script doesn't actually exit. I don't know in exactly what happens, but something stops working and if I can make sure there are no invalid commands, it works. – Wraper May 16 at 15:18
3

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.

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.