Bash関数から文字列値を返す方法


461

Bash関数から文字列を返したいのですが。

Javaで例を書いて、私が何をしたいのかを示します:

public String getSomeString() {
  return "tadaa";
}

String variable = getSomeString();

以下の例はbashで機能しますが、これを行うより良い方法はありますか?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

4
余談ですが、function funcName {POSIX以前のレガシー構文は初期のkshから継承されたものです(bashが尊重しない意味上の違いがあったため)。funcName() {なしでfunction、代わりに使用する必要があります。wiki.bash-hackers.org/scripting/obsoleteを
Charles Duffy

そのリンクは、NAME()COMPOUND-CMDまたは関数NAME {CMDSを使用することを示しています。}それでfunction myFunction { blah; }結構です。それはだfunction myFunction() { blah }それはすなわちキーワード機能付き括弧の使用、罰金ではありません。
ウィル

回答:


290

私が知るより良い方法はありません。Bashは、ステータスコード(整数)とstdoutに書き込まれた文字列のみを認識します。


15
+1 @ tomas-f:この関数「getSomeString()」の内容には十分注意する必要があります。最終的にエコーするコードがあると、誤った戻り文字列が返されることになるためです。
Mani

11
これはまったく間違っています。戻り変数内で任意のデータを返すことができます。これは明らかに良い方法です。
Evi1M4chine

36
@ Evi1M4chine、ええと...いいえ、できません。スクリプトで行っているように、グローバル変数を設定して「return」と呼ぶことができます。しかし、それは慣例によるものであり、実際にはプログラムでコードの実行に関連付けられていません。「明らかにより良い方法」?いいえ。コマンド置換ははるかに明示的でモジュール化されています。
ワイルドカード2016年

6
「コマンド置換ははるかに明示的でモジュール化されています」は、質問がコマンドに関するものである場合に関連します。この質問は、bash関数から文字列を返す方法です。OPが要求したことを実行するための組み込みの方法がBash 4.3(2014?)以降で利用可能です-以下の私の回答を参照してください。
zenaan 2016年

4
元の質問には、最も簡単な方法が含まれており、ほとんどの場合にうまく機能します。Bashの戻り値は、スクリプトの標準の戻り値ではなく、シェルの数値コマンドの終了コードに似ているため(おそらくsomefunction && echo 'success')関数を別のコマンドのように考える場合、それは理にかなっています。コマンドは、ステータスコード以外の終了時に何も「返さない」が、その間にキャプチャできるものを出力する場合がある。
Beejor 2017年

193

関数に変数を最初の引数として取り、返される文字列で変数を変更することができます。

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

「foo bar rab oof」を出力します。

編集:適切な場所に引用符を追加して、文字列内の空白が@Luca Borrioneのコメントに対応できるようにしました。

編集:デモとして、次のプログラムを参照してください。これは汎用的なソリューションです。ローカル変数に文字列を受け取ることもできます。

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar=''
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

これは印刷します:

+ return_var=
+ pass_back_a_string return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local lvar=
+ pass_back_a_string lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

編集:コメントで@Xichen Liが誤って批判したように、元の変数の値関数で使用できることを示します。

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "echo in pass_back_a_string, original $1 is \$$1"
    eval "$1='foo bar rab oof'"
}

return_var='original return_var'
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar='original lvar'
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

これは出力を与えます:

+ return_var='original return_var'
+ pass_back_a_string return_var
+ eval 'echo in pass_back_a_string, original return_var is $return_var'
++ echo in pass_back_a_string, original return_var is original return_var
in pass_back_a_string, original return_var is original return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local 'lvar=original lvar'
+ pass_back_a_string lvar
+ eval 'echo in pass_back_a_string, original lvar is $lvar'
++ echo in pass_back_a_string, original lvar is original lvar
in pass_back_a_string, original lvar is original lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

1
この答えは素晴らしいです!パラメータは、C ++のアイデアと同様に、参照によって渡すことができます。
Yun Huang

4
その回答について専門家から回答を受け取っていただければ幸いです。スクリプトで使用されているのを私は見たことがありません。とにかく、それは+1です。正解に投票されたはずです
ジョン

これはfgm簡単な方法で書かれた答えと同じではありませんか?文字列fooに空白が含まれている場合、これは機能しませんが、fgm「1」は表示されます。
Luca Borrione、2012年

4
@XichenLi:反対票を投稿していただきありがとうございます。私の編集を見てください。関数の変数の初期値は、を使用して取得できます\$$1。別のものを探している場合は、私に知らせてください。
bstpierre

1
@timiscodingこれはprintf '%q' "$var"。%qは、シェルエスケープのフォーマット文字列です。次に、そのまま渡します。
bb010g 2015

99

上記のすべての回答は、bashのmanページに記載されている内容を無視しています。

  • 関数内で宣言されたすべての変数は、呼び出し環境と共有されます。
  • ローカルで宣言されたすべての変数は共有されません。

コード例

#!/bin/bash

f()
{
    echo function starts
    local WillNotExists="It still does!"
    DoesNotExists="It still does!"
    echo function ends
}

echo $DoesNotExists #Should print empty line
echo $WillNotExists #Should print empty line
f                   #Call the function
echo $DoesNotExists #Should print It still does!
echo $WillNotExists #Should print empty line

そして出力

$ sh -x ./x.sh
+ echo

+ echo

+ f
+ echo function starts 
function starts
+ local 'WillNotExists=It still does!'
+ DoesNotExists='It still does!'
+ echo function ends 
function ends
+ echo It still 'does!' 
It still does!
+ echo

また、pdkshおよびkshの下でも、このスクリプトは同じことを行います。


10
この回答にはメリットがあります。関数から文字列を返したいと思ってここに来ました。この答えは、それが私のC#の習慣の話にすぎないことを認識させられました。他の人も同じ経験をしているのではないかと思います。
LOAS 2013

4
@ElmarZanderあなたは間違っている、これは完全に関連しています。これは、関数スコープの値をグローバルスコープに入れる簡単な方法であり、bstpierreで概説されているように、グローバル変数を再定義するためのevalアプローチよりも優れている/単純であると考える人もいます。
KomodoDave 2013年

localは非bashスクリプトに移植できません。これは、一部の人がそれを回避する1つの理由です。
明るい明るい

質問:ループ内の変数はどうですか?
anu 2015

1
Mac($ bash --version GNU bash、バージョン3.2.57(1)-release(x86_64-apple-darwin14)Copyright(C)2007 Free Software Foundation、Inc.)では、一致するグローバル変数が初期化されましたが、同じ変数を別の関数f2で副作用にしようとすると、その副作用は持続しません。だから、それは非常に一貫していないようであり、したがって私の使用には適していません。
AnneTheAgile

45

Bashはバージョン4.3以降、2014年2月(?)で、「eval」を超える参照変数または名前参照(namerefs)を明示的にサポートしており、同じ有益なパフォーマンスと間接効果を備えています。 「 'eval'を忘れてこのエラーを修正する必要がある」:

declare [-aAfFgilnrtux] [-p] [name[=value] ...]
typeset [-aAfFgilnrtux] [-p] [name[=value] ...]
  Declare variables and/or give them attributes
  ...
  -n Give each name the nameref attribute, making it a name reference
     to another variable.  That other variable is defined by the value
     of name.  All references and assignments to name, except for
     changing the -n attribute itself, are performed on the variable
     referenced by name's value.  The -n attribute cannot be applied to
     array variables.
...
When used in a function, declare and typeset make each name local,
as with the local command, unless the -g option is supplied...

そしてまた:

パラメーター

変数にnameref属性を割り当てて、-nオプションをdeclareまたはlocal組み込みコマンド(declareおよびlocalの説明を参照)に割り当てて、namerefまたは別の変数への参照を作成できます。これにより、変数を間接的に操作できます。nameref変数が参照または割り当てられるたびに、nameref変数の値で指定された変数に対して実際に操作が実行されます。namerefは通常、関数に引数として渡される名前を持つ変数を参照するためにシェル関数内で使用されます。たとえば、変数名が最初の引数としてシェル関数に渡された場合、

      declare -n ref=$1

関数内で、値が最初の引数として渡された変数名であるnameref変数refを作成します。refへの参照と代入は、名前が$ 1として渡された変数への参照と代入として扱われます。forループの制御変数にnameref属性がある場合、単語のリストはシェル変数のリストにすることができ、ループが実行されると、リスト内の各単語に対して名前参照が確立されます。配列変数に-n属性を指定することはできません。ただし、nameref変数は配列変数および添え字付き配列変数を参照できます。組み込みのunsetに-nオプションを使用すると、名前参照を設定解除できます。それ以外の場合、nameref変数の名前を引数としてunsetを実行すると、

たとえば(EDIT 2:(ありがとう、ロン)名前空間付き(接頭辞付き)の関数内部変数名。外部変数の衝突を最小限に抑えるために、最終的に適切に応答する必要があります。Karstenのコメントで指摘された問題):

# $1 : string; your variable to contain the return value
function return_a_string () {
    declare -n ret=$1
    local MYLIB_return_a_string_message="The date is "
    MYLIB_return_a_string_message+=$(date)
    ret=$MYLIB_return_a_string_message
}

この例をテストします:

$ return_a_string result; echo $result
The date is 20160817

組み込みのbash "declare"を関数で使用すると、宣言された変数がデフォルトで "local"になり、 "-n"も "local"と共に使用できることに注意してください。

「重要な宣言」変数と「退屈なローカル」変数を区別したいので、このように「宣言」と「ローカル」を使用すると、ドキュメントとして機能します。

編集1-(カルステンによる以下のコメントへの応答)-もうコメントを追加することはできませんが、カルステンのコメントは私に考えさせられたので、私はWORKS FINE、AFAICT-カルステンをテストしましたこれを読んだら、正確なセットを提供してください次のステップは問題なく機能するため、コマンドラインからテストステップを実行して、存在すると想定される問題を示します。

$ return_a_string ret; echo $ret
The date is 20170104

(上記の関数をbashの項に貼り付けた直後にこれを実行しました-ご覧のとおり、結果は正常に機能します。)


4
これがトップに浸透することを願っています。evalは最後の手段でなければなりません。注目に値するのは、nameref変数はbash 4.3(changelogによると)(2014年2月にリリースされた[?])以降でのみ使用できることです。移植性が懸念される場合、これは重要です。declare関数内でローカル変数を作成する(その情報はによって与えられないhelp declare)という事実については、bashマニュアルを引用してください。gオプションが指定されています... "
init_js

2
これには、evalソリューションと同じエイリアスの問題があります。関数を呼び出して出力変数の名前を渡す場合、呼び出す関数内でローカルに使用される変数の名前を渡さないようにする必要があります。関数の呼び出し元のいずれかがその名前を出力パラメーターに使用したい場合は、関数に新しいローカル変数を追加したり名前を変更したりすることができないため、これはカプセル化に関して大きな問題です。
Karsten 2017年

1
@Karstenは同意した。どちらの場合も(evalとnamerefs)、別の名前を選択する必要がある場合があります。evalに対するnamerefアプローチの利点の1つは、エスケープ文字列を処理する必要がないことです。もちろん、いつでものようなことをすることができますがK=$1; V=$2; eval "$A='$V'";、1つの間違い(たとえば、空のパラメータや省略されたパラメータ)はより危険です。@zenaan戻り変数名として「ret」ではなく「message」を選択した場合、@ Karstenによって提起された問題が適用されます。
init_js 2017年

3
関数は、おそらく最初からnameref引数を受け入れるように設計する必要があるため、関数の作成者は名前の衝突の可能性を認識し、いくつかの一般的な規則を使用してそれを回避できます。たとえば、関数X内で、ローカル変数に「X_LOCAL_name」という規則で名前を付けます。
ロンバーク2017

34

同様bstpierre以上、私が使用して明示的に命名出力変数を使用することをお勧めします:

function some_func() # OUTVAR ARG1
{
   local _outvar=$1
   local _result # Use some naming convention to avoid OUTVARs to clash
   ... some processing ....
   eval $_outvar=\$_result # Instead of just =$_result
}

$の引用の使用に注意してください。これにより、コンテンツ$resultをシェルの特殊文字として解釈することを回避できます。これは、エコーをキャプチャする慣用句より桁違いに速いことがわかりましたresult=$(some_func "arg1")。MSYSでbashを使用すると、速度の違いがさらに顕著になり、関数呼び出しからのstdoutキャプチャがほとんど破滅的になります。

ローカルはbashで動的にスコープされているため、ローカル変数を送信しても問題ありません。

function another_func() # ARG
{
   local result
   some_func result "$1"
   echo result is $result
}

4
これは、デバッグやロギングの目的で複数のエコーステートメントを使用したいので、役に立ちます。エコーをすべてキャプチャーするため、エコーをキャプチャーする慣用句は失敗します。ありがとうございました!
AnneTheAgile

これは(2番目に良い)適切なソリューションです!クリーン、高速、エレガント、実用的。
Evi1M4chine 2016年

+2それを現実に保つため。私は言っていた。echo関数の内部とコマンドの置換を組み合わせることを、どうしてそんなに多くの人が無視することができるのでしょう!
Anthony Rutledge

22

関数の出力をキャプチャすることもできます。

#!/bin/bash
function getSomeString() {
     echo "tadaa!"
}

return_var=$(getSomeString)
echo $return_var
# Alternative syntax:
return_var=`getSomeString`
echo $return_var

奇妙に見えますが、グローバル変数IMHOを使用するよりも優れています。パラメータを渡すことは通常どおりに機能し、中かっこまたはバックティックの内側に置くだけです。


11
代替構文の注記は別として、これは、opが自分の質問ですでに書いたものとまったく同じではありませんか?
Luca Borrione、2012年

12

他の人が書いたように、最も簡単で堅牢なソリューションは、コマンド置換を使用することです。

assign()
{
    local x
    x="Test"
    echo "$x"
}

x=$(assign) # This assigns string "Test" to x

これには別のプロセスが必要になるため、欠点はパフォーマンスです。

このトピックで提案されている他の手法、つまり引数として割り当てる変数の名前を渡すことには副作用があり、その基本的な形式ではお勧めしません。問題は、おそらく戻り値を計算するために関数内にいくつかの変数が必要になることであり、戻り値を格納することを目的とした変数の名前がそれらの1つと干渉する可能性があります。

assign()
{
    local x
    x="Test"
    eval "$1=\$x"
}

assign y # This assigns string "Test" to y, as expected

assign x # This will NOT assign anything to x in this scope
         # because the name "x" is declared as local inside the function

もちろん、関数の内部変数をローカルとして宣言しないこともできますが、そうでない場合は常にそうする必要があります。逆に、同じ名前を持つ親スコープから無関係な変数を誤って上書きしてしまう可能性があります。 。

考えられる回避策の1つは、渡された変数をグローバルとして明示的に宣言することです。

assign()
{
    local x
    eval declare -g $1
    x="Test"
    eval "$1=\$x"
}

名前 "x"が引数として渡されると、関数本体の2行目が前のローカル宣言を上書きします。ただし、名前自体は引き続き干渉する可能性があるため、戻り値を書き込む前に、渡された変数に以前に格納された値を使用する場合は、最初に別のローカル変数にコピーする必要があることに注意してください。そうでない場合、結果は予測できません。さらに、これはBASHの最新バージョン、つまり4.2でのみ機能します。より移植性の高いコードは、同じ効果を持つ明示的な条件構造を利用する場合があります。

assign()
{
    if [[ $1 != x ]]; then
      local x
    fi
    x="Test"
    eval "$1=\$x"
}

おそらく最もエレガントな解決策は、関数の戻り値用に1つのグローバル名を予約し、作成するすべての関数で一貫してそれを使用することです。


3
これ^^^。カプセル化を壊す不注意なエイリアシングは、evalおよびdeclare -nソリューションの両方での大きな問題です。resultすべての出力パラメーターのように単一の専用変数名を使用することによる回避策は、関数が競合を回避するために呼び出し元をすべて知る必要がない唯一の解決策のようです。
Karsten 2017年

12

前述のように、関数から文字列を返す「正しい」方法は、コマンド置換を使用することです。(@Maniが前述したように)関数もコンソールに出力する必要がある場合は、関数の先頭に一時的なfdを作成し、コンソールにリダイレクトします。文字列を返す前に、一時的なfdを閉じます。

#!/bin/bash
# file:  func_return_test.sh
returnString() {
    exec 3>&1 >/dev/tty
    local s=$1
    s=${s:="some default string"}
    echo "writing directly to console"
    exec 3>&-     
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

パラメータなしでスクリプトを実行すると、...

# ./func_return_test.sh
writing directly to console
my_string:  [some default string]

これが人々に役立つことを願っています

-アンディ


6
これには用途がありますが、全体として、コンソールへの明示的なリダイレクトは行わないでください。出力がすでにリダイレクトされているか、スクリプトがttyが存在しないコンテキストで実行されている可能性があります。3>&1スクリプトの先頭で複製してから、関数内の&1 &3別のプレースホルダーを操作することで、これを回避でき&4ます。でも醜いオールラウンド。
jmb 2014年

8

グローバル変数を使用できます:

declare globalvar='some string'

string ()
{
  eval  "$1='some other string'"
} # ----------  end of function string  ----------

string globalvar

echo "'${globalvar}'"

これは与える

'some other string'

6

の使用を避けるために、追加のファイル記述子を操作して、アンディの答えに私のコメントを説明するために/dev/tty

#!/bin/bash

exec 3>&1

returnString() {
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

まだ厄介ですが。


3

あなたがそれを持っている方法は、スコープを壊すことなくこれを行う唯一の方法です。Bashには戻り値の型の概念はありません。終了コードとファイル記述子(stdin / out / errなど)のみです。


3

次のコードを考慮して、Vicky Ronnenのヘッドアップに対処します。

function use_global
{
    eval "$1='changed using a global var'"
}

function capture_output
{
    echo "always changed"
}

function test_inside_a_func
{
    local _myvar='local starting value'
    echo "3. $_myvar"

    use_global '_myvar'
    echo "4. $_myvar"

    _myvar=$( capture_output )
    echo "5. $_myvar"
}

function only_difference
{
    local _myvar='local starting value'
    echo "7. $_myvar"

    local use_global '_myvar'
    echo "8. $_myvar"

    local _myvar=$( capture_output )
    echo "9. $_myvar"
}

declare myvar='global starting value'
echo "0. $myvar"

use_global 'myvar'
echo "1. $myvar"

myvar=$( capture_output )
echo "2. $myvar"

test_inside_a_func
echo "6. $_myvar" # this was local inside the above function

only_difference



あげる

0. global starting value
1. changed using a global var
2. always changed
3. local starting value
4. changed using a global var
5. always changed
6. 
7. local starting value
8. local starting value
9. always changed

多分通常のシナリオはtest_inside_a_func関数で使用される構文を使用することなので、ほとんどの場合、両方のメソッドを使用できますが、出力をキャプチャすることは、あらゆる状況で常に機能する安全なメソッドであり、関数からの戻り値を模倣できますVicky Ronnen正しく指摘されているように、他の言語で見つけてください。


2

オプションはすべて列挙されていると思います。どれを選択するかは、特定のアプリケーションに最適なスタイルの問題に帰着する可能性があります。そのため、私は役立つと思う特定のスタイルを1つ提供したいと思います。bashでは、変数と関数は同じ名前空間にありません。したがって、同じ名前の変数を関数の値として扱うことは、厳密に適用すると、名前の衝突を最小限に抑えて読みやすくするための慣例です。実際の例:

UnGetChar=
function GetChar() {
    # assume failure
    GetChar=
    # if someone previously "ungot" a char
    if ! [ -z "$UnGetChar" ]; then
        GetChar="$UnGetChar"
        UnGetChar=
        return 0               # success
    # else, if not at EOF
    elif IFS= read -N1 GetChar ; then
        return 0           # success
    else
        return 1           # EOF
    fi
}

function UnGetChar(){
    UnGetChar="$1"
}

そして、そのような関数の使用例:

function GetToken() {
    # assume failure
    GetToken=
    # if at end of file
    if ! GetChar; then
        return 1              # EOF
    # if start of comment
    elif [[ "$GetChar" == "#" ]]; then
        while [[ "$GetChar" != $'\n' ]]; do
            GetToken+="$GetChar"
            GetChar
        done
        UnGetChar "$GetChar"
    # if start of quoted string
    elif [ "$GetChar" == '"' ]; then
# ... et cetera

ご覧のとおり、戻りステータスは、必要なときに使用できるようになっています。不要な場合は無視してください。「返された」変数も同様に使用または無視できますが、もちろん関数が呼び出されたでなければなりません。

もちろん、これは慣例にすぎません。戻る前に関連する値を設定しなくても(関数の開始時に常にnullになるという私の慣習)、または関数を再度呼び出すことによって(おそらく間接的に)その値を踏みにじることは自由です。それでも、bash関数を頻繁に使用している場合、これは非常に便利な規則です。

これは「perlに移動する」などの兆候であるという感情とは対照的に、私の哲学は、あらゆる言語の複雑さを管理するために慣例が常に重要であるということです。


2

echo文字列にすることもできますが|、関数を他の何かにパイプ()することでキャッチできます。

あなたがそれを行うことができますexprが、ShellCheckは非推奨として、この使用状況を報告します。


問題は、パイプの右側がサブシェルであることです。だからmyfunc | read OUTPUT ; echo $OUTPUT何も生み出しません。myfunc | ( read OUTPUT; echo $OUTPUT )期待値を取得し、右側で何が起こっているかを明確にします。しかしもちろん、その後は必要な場所でOUTPUTを利用できません...
Ed Randall

2

呼び出し元が変数名を渡すことができる「名前付き出力変数」方式の主要な問題(evalまたはを使用するかどうかにかかわらdeclare -nず)は、不注意なエイリアス、つまり名前の衝突:カプセル化の観点から、追加または名前の変更ができないのはひどい関数のすべての呼び出し元を最初にチェックせずに関数内のローカル変数を使用して、出力パラメーターと同じ名前を渡したくないことを確認します。(または別の方向では、使用する予定の出力パラメーターがその関数のローカルではないことを確認するためだけに、呼び出している関数のソースを読み取る必要はありません。)

それを回避する唯一の方法は、REPLYEvi1M4chineによって提案されたような)単一の専用出力変数、またはRon Burkによって提案されたような規則を使用することです

ただし、次の例の関数で行ったように、関数で内部的に固定出力変数を使用し、上部に砂糖を追加してこの事実を呼び出し元から隠すことcallができます。これを概念実証と見なしますが、重要な点は

  • 関数は常に戻り値を REPLY通常どおり終了コードを返すこともできます
  • 呼び出し元の観点から、戻り値は(ローカルまたはグローバル)を含む任意の変数に割り当てることができます(例をREPLY参照wrapper)。関数の終了コードが渡されるため、たとえば、ifまたはwhileまたは類似の構造期待通りに動作します。
  • 構文的には、関数呼び出しはまだ1つの単純なステートメントです。

これがcall機能する理由は、関数自体にローカルがなくREPLY、以外の変数を使用しないため、名前の衝突の可能性を回避するためです。呼び出し元が定義した出力変数名が割り当てられた時点で、call呼び出される関数のスコープではなく、呼び出し元のスコープ(技術的には関数の同じスコープ)にいることになります。

#!/bin/bash
function call() { # var=func [args ...]
  REPLY=; "${1#*=}" "${@:2}"; eval "${1%%=*}=\$REPLY; return $?"
}

function greet() {
  case "$1" in
    us) REPLY="hello";;
    nz) REPLY="kia ora";;
    *) return 123;;
  esac
}

function wrapper() {
  call REPLY=greet "$@"
}

function main() {
  local a b c d
  call a=greet us
  echo "a='$a' ($?)"
  call b=greet nz
  echo "b='$b' ($?)"
  call c=greet de
  echo "c='$c' ($?)"
  call d=wrapper us
  echo "d='$d' ($?)"
}
main

出力:

a='hello' (0)
b='kia ora' (0)
c='' (123)
d='hello' (0)

1

スカラー配列値オブジェクトの両方を返すbashパターン:

定義

url_parse() { # parse 'url' into: 'url_host', 'url_port', ...
   local "$@" # inject caller 'url' argument in local scope
   local url_host="..." url_path="..." # calculate 'url_*' components
   declare -p ${!url_*} # return only 'url_*' object fields to the caller
}

呼び出し

main() { # invoke url parser and inject 'url_*' results in local scope
   eval "$(url_parse url=http://host/path)" # parse 'url'
   echo "host=$url_host path=$url_path" # use 'url_*' components
}

0

私のプログラムでは、慣例により、これが既存の$REPLY変数のread目的であり、正確な目的で使用されます。

function getSomeString {
  REPLY="tadaa"
}

getSomeString
echo $REPLY

このechoES

tadaa

ただし、競合を回避するために、他のグローバル変数はこれを行います。

declare result

function getSomeString {
  result="tadaa"
}

getSomeString
echo $result

それでも不十分な場合は、Markarian451のソリューションをお勧めします。


-2
agt@agtsoft:~/temp$ cat ./fc 
#!/bin/sh

fcall='function fcall { local res p=$1; shift; fname $*; eval "$p=$res"; }; fcall'

function f1 {
    res=$[($1+$2)*2];
}

function f2 {
    local a;
    eval ${fcall//fname/f1} a 2 3;
    echo f2:$a;
}

a=3;
f2;
echo after:a=$a, res=$res

agt@agtsoft:~/temp$ ./fc
f2:10
after:a=3, res=
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.