2

I have the following bash code (running on Red Hat) that is exiting when I enable set -o errexit and the variable in the code is empty, BUT works fine when the variable is set; the code is designed to test if a screen session matching .monitor_* exists, and if so do something.

I have the following turned on:

set -o errexit
set -x xtrace; PS4='$LINENO: '

If there is a session matching the above pattern it works; however, if nothing matches it just exits with no information other than the following output from xtrace

someuser:~/scripts/tests> ./if_test.sh
+ ./if_test.sh
+ PS4='$LINENO: '
4: set -o errexit
5: set -o pipefail
6: set -o nounset
88: /usr/bin/ls -lR /var/run/uscreens/S-storage-rsync
88: grep '.monitor_*'
88: awk '{ print $9 }'
88: /usr/bin/grep -Ev 'total|uscreens'
8: ms=

I tested the command I am using to set the ms var and it agrees with the xtrace output, it's not set.

someuser:~/scripts/tests> test -n "${mn}"
+ test -n ''

I have tried using a select statement and got the same results... I can't figure it out, anyone able to help? Thanks.

I read through all the possible solution recommendations, nothing seems to address my issue.

The code:

#!/usr/bin/env bash

set -o xtrace; PS4='$LINENO: '
set -o errexit
set -o pipefail
set -o nounset

ms="$(/usr/bin/ls -lR /var/run/uscreens/S-"${USER}" | /usr/bin/grep -Ev "total|uscreens" | grep ".monitor_*" | awk '{ print $9 }')"

if [[ -z "${ms}" ]]; then
    echo "Handling empty result"
elif [[ -n "${ms}" ]]; then
    echo "Handling non-empty result"
fi

The following answer was proposed: Test if a variable is set in bash when using "set -o nounset"; however, it doesn't address the issue at all. In my case the variable being tested is set and as stated in my detail, it's set to "", or nothing. Thank you; however, it doesn't help.

It really seems to be the variable declaration that it isn't liking.

ms="$(/usr/bin/ls -lR /var/run/uscreens/S-"${USER}" | /usr/bin/grep -Ev "total|uscreens" | grep ".monitor_*" | awk '{ print $9 }')"
8
  • Likely a duplicate of stackoverflow.com/questions/7832080/… Commented Aug 28, 2019 at 18:49
  • Possible duplicate of Test if a variable is set in bash when using "set -o nounset" Commented Aug 28, 2019 at 18:50
  • 1
    You're running set -o pipefail. When grep doesn't match anything, it has a nonzero exit status, and with pipefail, that fails the entire pipeline. This is all behaving exactly the way you're telling your shell it should behave. Commented Aug 28, 2019 at 20:32
  • 1
    BTW, before deciding to use set -o errexit, I strongly suggest going through the exercises in BashFAQ #105. Commented Aug 28, 2019 at 20:33
  • 1
    Well, my main suggestion is not to use set -e. You can, of course, always add a conditional to the specific pipeline component(s). Commented Aug 28, 2019 at 20:37

2 Answers 2

3
  • You're running set -o pipefail, so if any component in a pipeline has a nonzero exit status, the entire pipeline is treated as having a nonzero exit status.
  • Your pipeline runs grep. grep has a nonzero status whenever no matches are found.
  • You're running set -o errexit (aka set -e). With errexit enabled, the script terminates whenever any command fails (subject to a long and complicated set of exceptions; some of these are presented in the exercises section of BashFAQ #105, and others touched on in this excellent reference).

Thus, when you have no matches in your grep command, your script terminates on the command substitution running the pipeline in question.


If you want to exempt a specific command from set -e's behavior, the easiest way to do it is to simply append ||: (shorthand for || true), which marks the command as "checked".

Sign up to request clarification or add additional context in comments.

3 Comments

For those still learning or needing more detail on exactly how Charles' answer applies to my code, I modified my variable to apply the || true to it (full var shown above: ... grep ".monitor_*" | awk '{ print $9 }' || true)" Or, ... grep ".monitor_*" | awk '{ print $9 }' ||:)"
Consider awk '/total|uscreens/ && /.monitor_*/ { print $9 }', getting rid of the greps altogether.
...that said, do note that parsing ls output is itself best avoided.
1

"You're running set -o pipefail. When grep doesn't match anything, it has a nonzero exit status, and with pipefail, that fails the entire pipeline. This is all behaving exactly the way you're telling your shell it should behave." – Charles Duffy

Charles above comment was exactly what was going on, my script was working as intended and I need to adjust the logic to work differently if I wish to keep set -o pipefail set.

Thank you for the help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.