You are not logged in.
Pages: 1
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
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
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
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
Last edited by ayekat (2017-02-22 22:10:08)
Offline
Great, problem solved, marking thread as SOLVED.
Thank you!
Offline
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
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
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
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
Thanks. I am learning more and more neat tricks
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
Offline
Pages: 1