I was playing around with using trap
inside a function because of this question, and came up with this secondary question. Given the following code:
d() {
trap 'return' ERR
false
echo hi
}
If I run d
, the trap
causes the shell to return from the function without printing 'hi'. So far so good. But if I run it a second time, I get a message from the shell:
-bash: return: can only `return' from a function or sourced script
At first, I assumed this meant the ERR sig was happening twice: Once when false
gave a nonzero exit status (inside the function) and again when the function itself returned with a nonzero exit status (outside the function). But that hypothesis doesn't hold up against this test:
e() {
trap 'echo +;return' ERR
false
echo hi
}
If I run the above, no matter how often I run it, I no longer get the can only return
from a function or sourced script warning from bash. Why does the shell treat a compound command different from a simple command in a trap
arg?
My goal was to maintain the actual exit status of the command that caused the function to exit, but I think whatever is causing the above behavior also makes capturing the exit status complicated:
f() {
trap '
local s=$?
echo $s
return $s' ERR
false
echo hi
}
bash> f; echo $?
1
0
Wat? Can someone please explain why $s
expands to two different values here and, if it turns out to be the same cause, the above differentiation between return
and echo +; return
?
Your first conclusion was right: the ERR sig was happening twice.
During the first execution of 'd', you define a trap globally. This affect next commands (the current call of d is not affected). During the second execution of 'd', you define a trap again (not very useful), the call of 'false' fails, so we execute the handler defined by the trap. Then we return in the parent shell where 'd' fails too, so we execute the trap again.
Just a remark. ERR can be given as 'sigspec' argument, but ERR is not a signal ;-) From the manual of BASH:
With the function 'e', the ERR handler executes the 'echo' command that succeeds. That's why the 'e' function doesn't fail, that's why the ERR handler is not called twice in this case.
If you try "e; echo $?" you will read "0".
Then I tried your 'f' function. I observed the same behavior (and I was surprised). The cause is NOT a bad expension of "$s". If you try to hardcode a value, you should observe the argument given to the 'return' statement is ignored when it is executed in by the trap handler.
I don't know if it is a normal behavior or if it's a bug of BASH... Or maybe a trick to avoid an infinite loop in the interpreter :-)
By the way, it's not a good use of trap in my opinion. We can avoid side effect of trap by creating a sub-shell. In this case we avoid the error in the parent shell and we keep the exitcode of the inner function: