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.

2
  • grep -Fqx bax txt; (($? == 1)) && echo not found Commented May 27, 2020 at 0: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. Commented May 27, 2020 at 0:58

1 Answer 1

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
    ...
}
Sign up to request clarification or add additional context in comments.

2 Comments

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.