You are not logged in.

#1 2017-02-22 21:54:03

drtebi
Member
Registered: 2013-02-09
Posts: 126

[SOLVED] Bash "set -e" confusion

Hello,

I hope someone can help me with this apparently simple problem.

It's probably best explained by just showing you the code:

#!/usr/bin/env bash


#set -e   # "interferes" with for loop?

REQUIRED=( 'rsync' 'nonexistantprogram' 'tar' )

checkRequired() {
    echo -n ">>> Checking required programs: "
    local NOT_FOUND=()
    for X in "${REQUIRED[@]}"; do
        TMP=$(which "${X}" 2>&1)
        if [[ $? != 0 ]]; then
            NOT_FOUND+=("${X}")
        fi
    done
    if [[ ${#NOT_FOUND[@]} != 0 ]]; then
        echo 'error'
        echo "the following programs are required: ${NOT_FOUND[@]}"
        exit 1
    fi
    echo 'ok'
}

checkRequired

The function is supposed to check each program in $REQUIRED for existence with "which". If it doesn't exist, it's thrown into the list $NOT_FOUND. Then, if $NOT_FOUND is not 0 in length, the program exits with a message.

The problem is, that I would like to use 'set -e' in the script, so that when an error occurs in another place in the script, the script won't keep running havoc... but if I include the 'set -e' command on the top of the script, then the loop will cause the program to exit because 'which' returns a non-zero exit status.

Is there a way to avoid that? E.g. to have 'set -e' but still be able to check for a non-zero exit code of a command?

Last edited by drtebi (2017-02-22 22:15:17)

Offline

#2 2017-02-22 22:00:11

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,449
Website

Re: [SOLVED] Bash "set -e" confusion

Just put a `set +e` before the loop, and `set -e` again after the loop.

Or, alternatively, just relace the for loop with a simplification:

    for X in "${REQUIRED[@]}"; do
        which "${X}" > /dev/null 2>&1 || NOT_FOUND+=("${X}")
    done

"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#3 2017-02-22 22:08:09

drtebi
Member
Registered: 2013-02-09
Posts: 126

Re: [SOLVED] Bash "set -e" confusion

That was quick! Thank you, it works.

Just wondering though, as far as I understand, if the error occurs in an 'if' statement or in the condition of a loop, than 'set -e' would not stop the script.
Is it possible to put the 'which ...' command into an 'if' statement?

Offline

#4 2017-02-22 22:08:42

ayekat
Member
Registered: 2011-01-17
Posts: 1,589

Re: [SOLVED] Bash "set -e" confusion

You do not seem to be using the value of $TMP, so how about simply

    for X in "${REQUIRED[@]}"; do
        if ! which "${X}" >/dev/null 2>&1; then
            ...

--edit--
ninja'd twice - but well... yes, Trilby's suggestion uses less lines smile

Last edited by ayekat (2017-02-22 22:10:08)


pkgshackscfgblag

Offline

#5 2017-02-22 22:14:38

drtebi
Member
Registered: 2013-02-09
Posts: 126

Re: [SOLVED] Bash "set -e" confusion

Great, problem solved, marking thread as SOLVED.

Thank you!

Offline

#6 2017-02-22 22:20:32

drtebi
Member
Registered: 2013-02-09
Posts: 126

Re: [SOLVED] Bash "set -e" confusion

But just another question so I really understand what's going on...

If I do:

which "${X}" > /dev/null 2>&1 || NOT_FOUND+=("${X}")

Doesn't the 'which' command return an error, which in turn should cause 'set -e' to stop the script? Or is it the redirection to /dev/null that causes 'set -e' to ignore that?

Offline

#7 2017-02-22 22:21:35

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,449
Website

Re: [SOLVED] Bash "set -e" confusion

drtebi wrote:

Just wondering though, as far as I understand, if the error occurs in an 'if' statement or in the condition of a loop...

It's not really the conditional that makes it work, any time the final exit status of a pipeline is zero set -e will allow it to continue.  That's why the `which ... || ...` works because if which fails, as long as the array can be appended to, the pipeline exits with a zero.  This is covered in `man set`.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#8 2017-02-22 22:27:44

drtebi
Member
Registered: 2013-02-09
Posts: 126

Re: [SOLVED] Bash "set -e" confusion

OK, I understand now. I read 'man set', but found it a bit difficult to understand...

Interesting is also the 'set -o pipefail' option, which, if I am not mistaken, would cause the script to stop if the 'which' command fails.

I think I got it now, thanks again.

Offline

#9 2017-02-22 22:46:28

bulletmark
Member
From: Brisbane, Australia
Registered: 2013-10-22
Posts: 649

Re: [SOLVED] Bash "set -e" confusion

OP, you don't really need to make that an array. I would just do:

NOT_FOUND=""
REQUIRED="rsync nonexistantprogram tar"
for X in $REQUIRED; do
    type $X &>/dev/null || NOT_FOUND+=" $X"
done

I use type rather than which (e.g. see this and this). You specified bash so may as well use compact and neater looking &>.

Last edited by bulletmark (2017-02-22 22:47:28)

Offline

#10 2017-02-22 23:58:29

drtebi
Member
Registered: 2013-02-09
Posts: 126

Re: [SOLVED] Bash "set -e" confusion

Thanks. I am learning more and more neat tricks smile

Using mostly Python, it is a bit awkward using arrays in Bash. This 'for' loop would also do something different in Python... just need to make that switch in my head smile

Offline

Board footer

Powered by FluxBB