Behavior of the 'return' statement in Bash functions
I'm having trouble understanding the behavior of the return
built-in in Bash. Here is a sample script.
#!/bin/bash
dostuff() {
date | while true; do
echo returning 0
return 0
echo really-notreached
done
echo notreached
return 3
}
dostuff
echo returncode: $?
The output of this script is:
returning 0
notreached
returncode: 3
If, however, the date |
is removed from line 4, the output is as I expected:
returning 0
returncode: 0
It seems like the return
statement as used above is acting th开发者_如何学Pythone way I thought the break
statement ought to behave, but only when the loop is on the right hand side of a pipe. Why is this the case? I couldn't find anything to explain this behavior in the Bash man page or online. The script acts the same way in Bash 4.1.5 and Dash 0.5.5.
In the date | while ...
scenario, that while loop is executed in a subshell due to the presence of the pipe. Thus, the return statement breaks the loop and the subshell ends, leaving your function to carry on.
You'll have to reconstruct the code to remove the pipeline so that no subshells are created:
dostuff() {
# redirect from a process substitution instead of a pipeline
while true; do
echo returning 0
return 0
echo really-notreached
done < <(date)
echo notreached
return 3
}
If you return
inside a function, that function will stop executing but the overall program will not exit.
If you exit
inside a function, the overall program will exit.
You cannot return
in the main body of a Bash script. You can only return
inside a function or sourced script.
For example:
#!/usr/bin/env bash
function doSomething {
echo "a"
return
echo "b" # this will not execute because it is after 'return'
}
function doSomethingElse {
echo "d"
exit 0
echo "e" # this will not execute because the program has exited
}
doSomething
echo "c"
doSomethingElse
echo "f" # this will not execute because the program exited in 'doSomethingElse'
Running the above code will output:
a
c
d
But return
should terminate a function call, not a subshell. exit
is intended to terminate (sub)shell. I think, it's some undocumented bug/feature.
echo | return
typed in a commandline gives an error. That's correct -return
should be in a function.f(){ echo|return; }
is accepted in the Bash and Dash, butreturn
doesn't terminate a function call.
If return
terminates a subshell, it would work outside a function. So, the conclusion is: return
terminates a subshell in a function which is strange.
The thing is: the subshell is a separate process. It doesn't really have a way to say to the parent shell: "I'm exiting because of a return"
There is no such thing in the exit status, which is the only thing the parent shell gets.
To cover this interesting feature of Bash...
return inside if (or any control command with expression like if/while/...)
return inside if with simple and less simple expressions
The subshell explanation is good. Control returns out of the current subshell. Which might be the Bash function. Or it might be any of the nested control commands with an expression which has caused a subshell to be invoked.
for very simple expressions, e.g., "true" or "1 == 1" there is no subshell invoked. So return behaves as ~normal/expected~.
for less simple expressions, e.g., variable expanded and compared with something then return behaves like break;
Simple (no subshell) examples:
$ rtest () { if true; then echo one; return 2; echo two; fi; echo not simple; return 7; }
$ rtest
one
$ echo $?
2
$ rtest () { if [[ 1 == 1 ]] ; then echo one; return 2; echo two; fi; echo not simple; return 7; }
$ rtest
one
$ echo $?
2
$ rtest () { if [[ 1 =~ 1 ]] ; then echo one; return 2; echo two; fi; echo not simple; return 7; }
$ rtest
one
$ echo $?
2
$ rtest () { if $DO ; then echo one; return 2; echo two; else echo three; return 3; fi; echo not simple; return 7; }
$ rtest
one
$ echo $?
2
$ rtest () { if [[ $DO ]]; then echo one; return 2; echo two; else echo three; return 3; fi; echo not simple; return 7; }
$ rtest
three
$ echo $?
3
$ rtest () { if [[ $DO == 1 ]] ; then echo one; return 2; echo two; else echo three; return 3; echo four; fi; echo not simple; return 7; }
$ rtest; echo $?
one
2
$ DO=1; rtest; echo $?
one
2
$ DO=0; rtest; echo $?
three
3
Expression not simple and presuming subshell is invoked, return behaviour is like break;
Not simple (subshell) example ... =~
inside [[ ]]
:
$ rtest () { if [[ $DO =~ 1 ]] ; then echo one; return 2; echo two; fi; echo not simple; return 7; }
$ rtest
not simple
$ echo $?
7
精彩评论