私はbashスクリプトを使っていて、戻り値を表示するための関数を実行したいと思います。
function fun1(){
return 34
}
function fun2(){
local res=$(fun1)
echo $res
}
fun2
を実行しても "34"は表示されません。これはなぜでしょうか。
Bashにはreturn
ステートメントがありますが、それを指定できるのは関数自身のexit
ステータス(0
と255
の間の値、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 || fun2
がfun2
値を返す場合にのみ、fun1
が0
を実行するように、戻り値を使ってブール論理を使うことができることに注意してください。デフォルトの戻り値は、関数内で最後に実行された文の終了値です。
$(...)
は、その中に含まれるコマンドによって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
はそれを提供する簡単な方法です。
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
}
他の答えの問題は、複数の関数が呼び出しチェーンにあるときに上書きできるグローバルを使用するか、関数が診断情報を出力できないことを意味する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_val
をlocal
として宣言し、他のすべての関数はret_val
を介して値を返します。
UnixおよびLinuxに関する次の質問も参照してください。 シェル関数のローカル変数の範囲 。
別の、おそらく状況に応じてさらに良い解決策は、 ya.teckによる投稿 がlocal -n
を使用するものです。
関数が定義されているスクリプトで実行する場合は、次のようにします。
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"
}
これを達成するもう一つの方法は 名前の参照 (Bash 4.3以降が必要です)です。
function example {
local -n VAR=$1
VAR=foo
}
example RESULT
echo $RESULT
他の人の素晴らしい投稿への追加として、これらのテクニックをまとめた記事がここにあります: