4

I am trying to implement error handling in my shell script as described in the 2nd answer in Best practice to use $? in bash?

My script looks like this:

#!/bin/bash

try() {
    "$@"
    code=$?
    if [ $code -ne 0 ]
    then
        echo "oops $1 didn't work"
        exit 1
    fi
}

try myvar=$(mktemp -p ./)

The script exits with a

./test.sh: line 4: myvar=./tmp.scNLzO1DDi: No such file or directory
oops myvar=./tmp.scNLzO1DDi didn't work

Just,

myvar=$(mktemp -p ./)

of course works fine, and $myvar returns the full path and name of the temp file.

How can I get the statement to assign the name of the tmp file to the variable myvar, while still passing the entire statement and it's results to try() so try() can do what it needs to? Thanks.

  • I think your function could look like: try() { "$@" || ! echo "$1 returned $?" >&2 || exit; } – mikeserv Sep 9 '14 at 11:29
  • You might also want to look at set -e. – Patrick Sep 9 '14 at 12:46
  • @mikeserv: modifying as suggested results in the following out: ./test.sh: line 15: myvar=./tmp.6vHt78D2Pk: No such file or directory myvar=./tmp.6vHt78D2Pk returned 127 – Jim Walker Sep 9 '14 at 13:48
  • @JimWalker - so it's working as well as it was I guess. But you still need to do as Volker Siegel says below. In general you can't set a variable as a consequence of evaluating one - it's just that the shell's bound to do one of them first, and it doesn't backtrack unless explicitly told to. That's what eval does. It is something you need to be careful with. – mikeserv Sep 9 '14 at 13:53
  • @mikeserv - understood. I was trying to avoid using eval due to the purported security implications, I guess I don't have much of a choice in this case. – Jim Walker Sep 9 '14 at 13:58
1

I think you want to use an error trap instead of a wrapper around evaluation.

err_handler () {
    code=$?
    if [ $code -ne 0 ]
    then
        echo "oops $1 didn't work"
        exit 1
    fi
}

trap 'err_handler' ERR
myvar=$(mktemp -p ./)
trap ERR
  • could you please explain why this approach is better than the try() method? Also, will this approach require a 'trap ERR' statement after every line in the script that needs to be checked for success or just a single 'trap ERR' statement at the end of the script? thanks. – Jim Walker Sep 14 '14 at 12:49
  • With the try method, you need to use eval to execute arbitrary code, which can be difficult to do correctly and safely. In the above, I only used the pair of trap statements to more closely simulate your use of try, which would only apply the error handler to a single statement. If your handler is general enough, you can enable it with the first trap and leave it enabled for the remainder of the script. Note, though, that traps are not inherited by subshells. – chepner Sep 15 '14 at 12:46
  • Thank you. While the previous answer did solve my original question, I like this approach better. There were way too many issues using eval while passing parameters to functions, subshells, etc. with the try() method. Setting a trap and handling it is a lot easier, and I was able to fine tune to the point where the trap nicely handles all (or most) error conditions. Where subshells are involved, I went with manual handling, still better than preceding every line with a try(). Thanks for both answers though. – Jim Walker Sep 27 '14 at 15:46
4

With try myvar=$(mktemp -p ./), the subshell with the mktmp is executed before try is called, during building the argument list for try.

To make the subshell be expanded later, you need to quote the argument of try:

try 'myvar=$(mktemp -p ./)'

To do the expansion later, you need to use eval "$@" instead of the "$@" in the first line of try.

  • Adding the eval to "$@" and quoting the try argument works perfectly. Thanks. – Jim Walker Sep 9 '14 at 13:50
  • On second thoughts, is there any way at all to do this without using eval? My concern is that the try() function will be used with pretty much every statement of the script, along with the fact that "$@" encompasses all arguments coming in, which would significantly increase the security exposure in this case. – Jim Walker Sep 9 '14 at 14:06
  • What you want is "execute the shell command in the args of try". The security aspect is independant of how you do that, with eval or some other way. It's the main point of try to execute the arguments (and see what happens) - no way around that. – Volker Siegel Sep 9 '14 at 14:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.