web-dev-qa-db-ja.com

Bash関数内の戻り値

私はbashスクリプトを使っていて、戻り値を表示するための関数を実行したいと思います。

function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}

fun2を実行しても "34"は表示されません。これはなぜでしょうか。

223
mindia

Bashにはreturnステートメントがありますが、それを指定できるのは関数自身のexitステータス(0255の間の値、0は「成功」を意味します)だけです。だからreturnはあなたが望むものではありません。

あなたのreturnステートメントをechoステートメントに変換したいかもしれません - そのようにしてあなたの関数出力は$()中括弧を使って捉えることができます。

これが一例です。

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

戻り値を取得するもう1つの方法(単に0から255の整数を返したい場合)は$?です。

function fun1(){
  return 34
}

function fun2(){
  fun1
  local res=$?
  echo $res
}

また、fun1 || fun2fun2値を返す場合にのみ、fun10を実行するように、戻り値を使ってブール論理を使うことができることに注意してください。デフォルトの戻り値は、関数内で最後に実行された文の終了値です。

289
tamasgal

$(...)は、その中に含まれるコマンドによってstdoutに送信されたテキストをキャプチャします。 returnは標準出力に出力されません。 $?には、最後のコマンドの結果コードが含まれています。

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}

Bashの関数は他の言語のような関数ではありません。それらは実際にはコマンドです。したがって、関数はあたかもあなたのパスから取り出されたバイナリまたはスクリプトであるかのように使用されます。プログラムロジックの観点から見ると、実際には違いはありません。

シェルコマンドはパイプ(別名ストリーム)で接続されており、「実際の」プログラミング言語のように基本的なデータタイプやユーザー定義のデータタイプではありません。コマンドの戻り値のようなものはありません。おそらくそれを宣言する本当の方法がないからでしょう。これはmanページ、またはコマンドの--help出力で発生する可能性がありますが、どちらも人間が読める形式のものであり、したがって風に書かれています。

コマンドが入力を取得したい場合、コマンドは入力ストリームまたは引数リストからそれを読み取ります。どちらの場合も、テキスト文字列を解析する必要があります。

コマンドが何かを返したいときは、それをその出力ストリームにechoする必要があります。よく実行されるもう1つの方法は、戻り値を専用のグローバル変数に格納することです。出力ストリームへの書き込みは、バイナリデータも取ることができるため、より明確で柔軟性があります。たとえば、BLOBを簡単に返すことができます。

encrypt() {
    gpg -c -o- $1 # encrypt data in filename to stdout (asks for a passphrase)
}

encrypt public.dat > private.dat # write function result to file

他の人がこのスレッドに書いたように、呼び出し側は出力を取り込むためにコマンド置換$()を使うこともできます。

並行して、この関数はgpg(GnuPG)の終了コードを "返す"でしょう。終了コードは他の言語が持っていないボーナスとして、またはあなたの気質によってはシェル関数の "Schmutzeffekt"として考えることができます。このステータスは、慣例により、成功した場合は0、それ以外の場合は1〜255の範囲の整数です。これを明確にするために:returnは(exitのように)0から255までの値しか取ることができず、0以外の値は必ずしも主張されるように必ずしもエラーではありません。

returnに明示的な値を与えないと、ステータスはBashステートメント/関数/コマンドなどの最後のコマンドから取られます。ですから、常にステータスがあり、returnはそれを提供する簡単な方法です。

42

returnステートメントは関数の終了コードを設定します。これはexitがスクリプト全体に対して行うのとほぼ同じです。

最後のコマンドの終了コードは、常に$?変数で使用できます。

function fun1(){
  return 34
}

function fun2(){
  local res=$(fun1)
  echo $? # <-- Always echos 0 since the 'local' command passes.

  res=$(fun1)
  echo $?  #<-- Outputs 34
}
23
Austin Phillips

他の答えの問題は、複数の関数が呼び出しチェーンにあるときに上書きできるグローバルを使用するか、関数が診断情報を出力できないことを意味するechoのいずれかを使用することです。結果」、つまり戻り値には、発信者が予想するよりも多くの情報が含まれており、奇妙なバグにつながります)、またはevalが非常に重くてハックです。

これを行う適切な方法は、最上位のものを関数に入れ、bashの動的スコープ規則でlocalを使用することです。例:

func1() 
{
    ret_val=hi
}

func2()
{
    ret_val=bye
}

func3()
{
    local ret_val=nothing
    echo $ret_val
    func1
    echo $ret_val
    func2
    echo $ret_val
}

func3

この出力

nothing
hi
bye

動的スコープは、ret_valが呼び出し元に応じて異なるオブジェクトを指すことを意味します!これは、ほとんどのプログラミング言語で使用されているレキシカルスコープとは異なります。これは実際には ドキュメント化された機能 であり、見逃しやすいだけで、あまり説明されていませんが、ここにドキュメントがあります(強調は私のものです):

関数に対してローカルな変数は、ローカルの組み込みで宣言できます。これらの変数は、関数およびそれが呼び出すコマンドのみに表示されます。

C/C++/Python/Java/C#/ javascriptのバックグラウンドを持つ人にとって、これはおそらく最大のハードルです。bashの関数は関数ではなく、コマンドであり、そのように振る舞います:stdout /に出力できますstderr、パイプでの入出力が可能で、終了コードを返すことができます。基本的に、スクリプトでコマンドを定義することと、コマンドラインから呼び出すことができる実行可能ファイルを作成することに違いはありません。

したがって、次のようにスクリプトを書く代わりに:

top-level code 
bunch of functions
more top-level code

次のように書きます:

# define your main, containing all top-level code
main() 
bunch of functions
# call main
main  

main()ret_vallocalとして宣言し、他のすべての関数はret_valを介して値を返します。

UnixおよびLinuxに関する次の質問も参照してください。 シェル関数のローカル変数の範囲

別の、おそらく状況に応じてさらに良い解決策は、 ya.teckによる投稿local -nを使用するものです。

9
Oliver

関数が定義されているスクリプトで実行する場合は、次のようにします。

POINTER= # used for function return values

my_function() {
    # do stuff
    POINTER="my_function_return"
}

my_other_function() {
    # do stuff
    POINTER="my_other_function_return"
}

my_function
RESULT="$POINTER"

my_other_function
RESULT="$POINTER"

私が望むなら、私は自分の関数にechoステートメントを含めることができるので、これが好きです。

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}
5
doc

これを達成するもう一つの方法は 名前の参照 (Bash 4.3以降が必要です)です。

function example {
  local -n VAR=$1
  VAR=foo
}

example RESULT
echo $RESULT
4
ya.teck

他の人の素晴らしい投稿への追加として、これらのテクニックをまとめた記事がここにあります:

  • グローバル変数を設定する
  • 関数に渡した名前のグローバル変数を設定します。
  • リターンコードを設定します(そして$でピックアップします)。
  • いくつかのデータを 'エコー'(そしてMYVAR = $(myfunction)でそれを拾います)

Bash関数から値を返す

3
Tom Hundt