0

This question is a follow-up to Bash conditional based on exit code of command .

I understand, and have made use of the accepted answer to that question:

$ cat ./txt
foo
bar
baz
$ if ! grep -Fx bax txt &>/dev/null; then echo "not found"; fi
not found
$

But I can't figure out the syntax to pull off a seemingly simple twist to the premise: how can I write a conditional for a specific exit code of a command?

This post pointed out that grep might fail with error code > 1; so I wanted to write the above conditional to print "not found" specifically if grep returned error code 1. How can I write such a statement? The following don't seem to work (I'm obviously flailing):

$ # Sanity-check that grep really fails with error code 1:
$ grep -Fx bax txt &>/dev/null
$ echo $?
1
$
$
$ if grep -Fx bax txt &>/dev/null -eq 1; then echo "not found"; fi
$ if grep -Fx bax txt &>/dev/null == 1; then echo "not found"; fi
$ if grep -Fx bax txt &>/dev/null = 1; then echo "not found"; fi
$ if [ grep -Fx bax txt &>/dev/null -eq 1 ]; then echo "not found"; fi
$ if [ grep -Fx bax txt &>/dev/null == 1 ]; then echo "not found"; fi
$ if [ grep -Fx bax txt &>/dev/null = 1 ]; then echo "not found"; fi
$

Note: I'm specifically trying to avoid running the command first, then using $? in the conditional, for the reasons pointed out by the accepted answer to the first noted post.

StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • `grep -Fqx bax txt; (($? == 1)) && echo not found` – Jetchisel May 27 '20 at 00:55
  • Although there is nothing wrong with the code you posted, it will execute the echo if the value of `$?` is not zero which means false, since testing the value of `$?` is not needed if you're just interested in `1` as the value. – Jetchisel May 27 '20 at 00:58

1 Answers1

1

If you want to respond differently to different error codes (as opposed to just success/failure, as in the linked question), you need to run the command and then check $? to get its specific exit status:

grep -Fx bax txt &>/dev/null
if [ $? -eq 1 ]; then
    echo "not found"
fi

(In my answer to the linked question, I described testing whether $? is zero as cargo cult programming; this does not apply to checking it for specific nonzero values.)

Note that $? is re-set for every command that runs -- including one that tests $? from the previous command -- so if you want to do more than one thing with it you must immediately store it in a variable, then run your tests on that variable:

curl "$url"
curl_status=$?
if [ "$curl_status" -eq 6 ]; then
    echo "Couldn't resolve host name"
elif [ "$curl_status" -eq 7 ]; then
    echo "Couldn't connect to host"
elif [ "$curl_status" -ne 0 ]; then
    echo "Some sort of error occurred; curl status was $curl_status"
fi

Note that using ! to negate the success/failure of a command effectively destroys the specific error code, because it converts all nonzero codes to the same result: zero. If you want to do the equivalent of if ! command ... and still have access to the specific code, my favorite idiom is to use || and a brace group, like this:

curl "$url" || {
    curl_status=$?
    ...
    # Do error handling/reporting based on $curl_status
    ...
}
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • Neat: an answer from the answerer to my original reference! :) Why is it that in this situation, one does need to resort to `$?` whereas if only testing for zero/nonzero, you can do it in a one-liner, and without `$?` ? I'm most experienced with C/C++, and I guess I'm expecting some form of analog to the concept of a rvalue/temporary variable - does that concept not apply here? – StoneThrow May 27 '20 at 01:21
  • 1
    @StoneThrow It's because `if` (and `while` and `until` and ...) inherently test zero/nonzero, so writing an explicit zero test is redundant; but they *don't* test for specific nonzero statuses, so for those tests you do have to write an explicit test. – Gordon Davisson May 27 '20 at 01:25