Executing the exit in a subshell is one pitfall. Even more puzzling is thwarting the effect of exit by wrapping it into local builtin like in the following script:
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
The script prints 42.
Here, exits from the subshell with return code exit1 does not abort, and continues with the script because. Even replacing the call by echo $(calcCALC) || exit 1 does not help because the return code of echo is executed in a subshell. Thank you, mikeserv, for pointing out that0 regardless of the return code of calc. For details seeAnd exit wrapped by local.calc is executed prior to echo.
Even more puzzling is thwarting the effect of exit by wrapping it into local builtin like in the following script. I stumbled over the problem when I wrote a function to verify an input valuesvalue. Example:
I want to create a file named "year month day.log", i.e., 20141211.log for today. The date is input by a user who may fail to provide a reasonable value. Therefore, in my function fname I check the return value of date to verify the validity of the user
input input:
#!/bin/bash
doit ()
{
local FNAME=$(fname "$1") || exit 1
touch "${FNAME}"
}
fname ()
{
date +"%Y%m%d.log" -d"$1" 2>/dev/null
if [ "$?" != 0 ] ; then
echo "check_date"fname reports \"Illegal Date\"" >&2
exit 1
fi
}
doit "$1"
check_datefname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
The line check_date…fname… says that the bad input data has been detected in the subshell. But the exit 1 at the end of the local … line is never triggered because the local directive always return 0. This is because local is executed after $(fname) and thus overwrites its return code. And because of that, the script continues and invokes touch with an empty parameter. This example is simple but the behavior of bash can be quite confusing in a real application. I know, real programmers don't use locals.☺
The strange behavior conforms to the documentation of local within the man page of bash: "The return status is 0 unless local is used outside a function, an invalid name is supplied, or name is a readonly variable."
Though not being a bug I feel that the behaviour of bash is counterintuitive. I am aware of the sequence of execution, local should not mask a broken assignment, nevertheless.
My initial answer contained some inaccurancies. After a revealing and in-depth discussion with mikeserv (thank you for that) I went for fixing them.