开发者

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, but return 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.

  1. for very simple expressions, e.g., "true" or "1 == 1" there is no subshell invoked. So return behaves as ~normal/expected~.

  2. 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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜