web-dev-qa-db-ja.com

bash関数でのreturnステートメントの動作

Bashに組み込まれているreturnの動作を理解するのに問題があります。これがサンプルスクリプトです。

#!/bin/bash

dostuff() {
    date | while true; do
        echo returning 0
        return 0
        echo really-notreached
    done

    echo notreached
    return 3
}

dostuff
echo returncode: $?

このスクリプトの出力は次のとおりです。

returning 0
notreached
returncode: 3

ただし、date |が4行目から削除され、出力は予想どおりです。

returning 0
returncode: 0

上で使用したreturnステートメントは、breakステートメントが動作するはずだと思ったように動作しているようですが、ループがパイプの右側にある場合に限ります。なぜそうなのですか?この動作を説明するものは、bashのmanページまたはオンラインで見つかりませんでした。スクリプトは、bash4.1.5とダッシュ0.5.5でも同じように機能します。

20
A B

の中に date | while ...シナリオでは、パイプが存在するため、whileループがサブシェルで実行されます。したがって、returnステートメントはループを中断し、subshel​​lが終了し、関数を続行します。

サブシェルが作成されないように、パイプラインを削除するコードを再構築する必要があります。

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
}
21
glenn jackman

ただし、returnは、サブシェルではなく、関数呼び出しを終了する必要があります。 exitは、(サブ)シェルを終了することを目的としています。文書化されていないバグ/機能だと思います。

  • コマンドラインに入力した_echo|return_はエラーになります、それは正しいです-returnは関数内にある必要があります。
    • f(){ echo|return; }はbash/dashで受け入れられますが、returnは関数呼び出しを終了しません。

      returnがサブシェルを終了する場合、それは関数の外部で機能します。したがって、結論は次のとおりです。returnは関数のサブシェルを終了しますこれは奇妙です。

  • 2
    motas

    関数内でreturnすると、その関数は実行を停止しますが、プログラム全体は終了しません。

    関数内でexitすると、プログラム全体が終了します。

    Bashスクリプトの本体ではreturnできません。関数またはソース・スクリプト内ではreturnしかできません。


    例えば:

    #!/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'
    

    上記のコードを実行すると、次のように出力されます。

    a
    c
    d
    
    2
    Derek Soike

    重要なのは、サブシェルは別のプロセスであるということです。親シェルに「リターンのために終了します」と言う方法は実際にはありません。

    親シェルが取得する唯一のものである終了ステータスには、そのようなものはありません。

    1
    pgas

    Bashのこの興味深い機能をカバーするための回答を追加します。 。 。

    • if(またはif/while/...のような式を持つ任意の制御コマンド)の内部に戻る

    • 単純な式とそれほど単純でない式を使用する場合は、内部に戻ります

    サブシェルの説明は良いです。コントロールは現在のサブシェルから戻ります。これはbash関数かもしれません。または、サブシェルが呼び出される原因となった式を持つネストされた制御コマンドのいずれかである可能性があります。

    1. 非常に単純な式の場合(例: 「true」または「1 == 1」では、サブシェルは呼び出されません。したがって、returnは〜normal/expected〜として動作します。

    2. あまり単純でない式の場合(例:変数が展開され、何かと比較されると、returnはbreakのように動作します。

    単純な(サブシェルなし)例:

    $ 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
    

    式は単純ではなく、サブシェルが呼び出されると想定しているため、戻り動作はブレークのようなものです。

    NOT SIMPLE(サブシェル)の例。 。 =〜内部[[]]:

    $ rtest () { if [[ $DO =~ 1 ]] ; then echo one; return 2; echo two; fi; echo not simple; return 7; }
    $ rtest
    not simple
    $ echo $?
    7
    
    0
    gaoithe