Bash関数の戻り値


305

私はbashスクリプトを使用していて、戻り値を出力する関数を実行したいと思います。

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

を実行するfun2と「34」が表示されません。これはなぜですか?


8
returnあなたの場合、基本的にはexit codeからの範囲と同じ0 - 255です。echo@septiの提案に従って使用します。終了コードはでキャプチャできます$?
devnull 2013年

1
この場合、fun1ですでにechoを使用する方がはるかに柔軟です。-他の機能にパイプされ、直接またはエコーが、その後のres = $(FUN1)と他の機能によって再利用することができ、標準出力に結果を送信します。それは、UNIXプログラミングのアイデアだfunction a() { echo 34; } function b() { while read data; do echo $data ; done ;} a | b
アルネBabenhauserheide

これを行う適切な方法は、関数に最上位のものを置き、bashの動的スコープ規則でローカルを使用することです。デモの答えを作成します。これはよく知られた機能ではなく、完全にサポートされている機能です。
オリバー


回答:


374

bashがありますがreturn文を、あなたはそれを指定することができる唯一のことは、関数自身のあるexit状態(間の値0255「成功」を意味、0)。だからreturnあなたが望むものではありません。

returnステートメントをステートメントに変換するechoこともできます。その方法では、$()中括弧を使用して関数の出力をキャプチャできます。

次に例を示します。

function fun1(){
  echo 34
}

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

戻り値を取得する別の方法(単に0〜255の整数を返したい場合)は、$?です。

function fun1(){
  return 34
}

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

また、戻り値を使用してブール値のロジックを使用できるので、値が返された場合にfun1 || fun2のみ実行されることに注意してください。デフォルトの戻り値は、関数内で実行された最後のステートメントの終了値です。fun2fun10


2
実行する必要がありfun1、戻り値はに格納され$?ます。私はそんなことはお勧めしませんが…
tamasgal 2013年

9
なぜ使用しないの$?ですか?
ピティコ2014

147
いいえ、いまいましい戻り値が必要です。エコーで地獄に。
トマーシュZato -復活モニカ

7
この環境で@Blauhirnをこの||構成で使用すると、終了コード0は成功と見なされるため、「true」になります。ゼロ以外はエラーなので、偽です。fun1 || fun2実際の戻り値自体の演算子ではなく、「fun1が成功を返すかfun2が成功を返す場合」の省略表現と考えてください。
davidA 2016年

6
厄介なのは、データを提供する必要がある関数が他のデータをstdoutにエコーすることもできないことです。これは、$()を使用する呼び出し元も受信し、混乱するか、出力を解析する必要があるためです。入れ子になっていてデータが失われる可能性がある2つの場所で同じグローバル変数を使用するのは時間の問題であるため、グローバル変数は素晴らしいものではありません。データの印刷とデータの送り返しには別々のチャネルが必要です。
オリバー

68

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

fun1 (){
  return 34
}

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

6
はいreturnは、設定$?で使用されますexit status。上記の例では、fun1「sがexit statusあろう34。また、$(...)指定されたコマンドからのstdoutに加えて、stderrもキャプチャすることに注意してください。
swoop81 2017年

59

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

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

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

コマンドが何かechoをその出力ストリームに返す必要がある場合。別のよく行われる方法は、戻り値を専用のグローバル変数に格納することです。出力ストリームへの書き込みは、バイナリデータも使用できるため、より明確で柔軟です。たとえば、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それを提供する簡単な方法にすぎません。


4
関数とコマンドを説明し、これがデータを発信者に送り返すという概念にどのように影響するかを説明するための+1
Oliver

4
シェルプログラミングはパイプを介してコマンドを接続することであることを説明するための+1。他のプログラミング言語は、戻り値の型を介して関数を構成します。Bashはテキストのストリームを介してコマンドを作成します。
jrahhali 2018年

29

この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
}

21

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

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

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に出力でき、パイプインできます/ out、終了コードを返すことができます。基本的に、スクリプトでコマンドを定義することと、コマンドラインから呼び出すことができる実行可能ファイルを作成することには違いはありません。

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

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です。


17

これを実現する別の方法は、名前参照です(Bash 4.3以降が必要です)。

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

example RESULT
echo $RESULT

3
誰が何を-n <name>=<reference>しているのか不思議に思っています:新しく作成された変数を、が指す別の変数への参照にし<reference>ます。へのさらなる代入<name>は、参照された変数に対して実行されます。
Valerio

7

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

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"

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

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

5

他の優れた投稿へのアドオンとして、これらのテクニックをまとめた記事を次に示します。

  • グローバル変数を設定する
  • 関数に渡した名前のグローバル変数を設定する
  • 戻りコードを設定します($で受け取りますか?)
  • データを「エコー」します(MYVAR = $(myfunction)で取得します)

Bash関数から値を返す


記事ではすべてのオプションについて明確に説明しているため、これが最良の答えです。
mzimmermann

-2

複数の戻り値に配列を使用するWindows上のGit Bash

バッシュコード:

#!/bin/bash

##A 6-element array used for returning
##values from functions:
declare -a RET_ARR
RET_ARR[0]="A"
RET_ARR[1]="B"
RET_ARR[2]="C"
RET_ARR[3]="D"
RET_ARR[4]="E"
RET_ARR[5]="F"


function FN_MULTIPLE_RETURN_VALUES(){

   ##give the positional arguments/inputs
   ##$1 and $2 some sensible names:
   local out_dex_1="$1" ##output index
   local out_dex_2="$2" ##output index

   ##Echo for debugging:
   echo "running: FN_MULTIPLE_RETURN_VALUES"

   ##Here: Calculate output values:
   local op_var_1="Hello"
   local op_var_2="World"

   ##set the return values:
   RET_ARR[ $out_dex_1 ]=$op_var_1
   RET_ARR[ $out_dex_2 ]=$op_var_2
}


echo "FN_MULTIPLE_RETURN_VALUES EXAMPLES:"
echo "-------------------------------------------"
fn="FN_MULTIPLE_RETURN_VALUES"
out_dex_a=0
out_dex_b=1
eval $fn $out_dex_a $out_dex_b  ##<--Call function
a=${RET_ARR[0]} && echo "RET_ARR[0]: $a "
b=${RET_ARR[1]} && echo "RET_ARR[1]: $b "
echo
##----------------------------------------------##
c="2"
d="3"
FN_MULTIPLE_RETURN_VALUES $c $d ##<--Call function
c_res=${RET_ARR[2]} && echo "RET_ARR[2]: $c_res "
d_res=${RET_ARR[3]} && echo "RET_ARR[3]: $d_res "
echo
##----------------------------------------------##
FN_MULTIPLE_RETURN_VALUES 4 5  ##<---Call function
e=${RET_ARR[4]} && echo "RET_ARR[4]: $e "
f=${RET_ARR[5]} && echo "RET_ARR[5]: $f "
echo
##----------------------------------------------##


read -p "Press Enter To Exit:"

期待される出力:

FN_MULTIPLE_RETURN_VALUES EXAMPLES:
-------------------------------------------
running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[0]: Hello
RET_ARR[1]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[2]: Hello
RET_ARR[3]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[4]: Hello
RET_ARR[5]: World

Press Enter To Exit:
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.