シェルスクリプトで呼び出された関数からの戻り値


126

シェルスクリプトで呼び出された関数から値を返したいのですが。構文が足りない可能性があります。グローバル変数を使ってみました。しかし、それもうまくいきません。コードは次のとおりです。

lockdir="somedir"
test() {
    retval=""

    if mkdir "$lockdir"
        then    # Directory did not exist, but it was created successfully
            echo >&2 "successfully acquired lock: $lockdir"
            retval="true"
        else
            echo >&2 "cannot acquire lock, giving up on $lockdir"
            retval="false"
    fi
    return retval
}


retval=test()
if [ "$retval" == "true" ]
    then
        echo "directory not created"
    else
        echo "directory already created"
fi

あなたの質問とは関係ありませんが、とにかくロックを取得しようとしている場合は、「lockfile」コマンドを使用できます。
ビクトルHerraiz

回答:


277

Bash関数は、望みどおりに文字列を直接返すことはできません。次の3つのことができます。

  1. 文字列をエコーする
  2. 文字列ではなく数値である終了ステータスを返します
  3. 変数を共有する

これは他のいくつかのシェルにも当てはまります。

これらの各オプションを実行する方法は次のとおりです。

1.エコーストリング

lockdir="somedir"
testlock(){
    retval=""
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval="true"
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval="false"
    fi
    echo "$retval"
}

retval=$( testlock )
if [ "$retval" == "true" ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2.終了ステータスを返す

lockdir="somedir"
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
    return "$retval"
}

testlock
retval=$?
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

3.変数を共有する

lockdir="somedir"
retval=-1
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
}

testlock
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2
functionキーワードを使用してbash関数を定義しないでください。それは携帯性を低下させます。それを削除します。
dimir、2012年

2
3番目の例では、retvalは環境変数ではありません。これは単にシェル変数です。エクスポートした場合のみ、環境変数になります。おそらく、3番目の例のタイトルは、「環境変数」ではなく「グローバル変数」になるはずです。
ウィリアムパーセル、2012年

4
2番目の例では、$?から割り当てるのではなく、 "if testlock; then ..."と書く方が慣用的です
William Pursell

@WilliamPursell間違った「環境」という言葉を削除しました。"$"を保持しましょう 教育目的のため。私はWikiコミュニティを有効にしたので、答えを自由に改善できます;-)
olibre

1
@ManuelJordan、関数は終了コードと>&2ログのみをstderrorに返すことができるため、最後のエコーはstdoutに書き込まれるため、呼び出し元の関数はstdoutのみをキャプチャし、stderrはキャプチャしません。実行がシングルスレッドであるとすると、より良いオプションは、testlockを呼び出した後に誰でも使用できるTEST_LOCK_STATUS = ""外部メソッド固有のカスタム変数を維持し、メソッドの開始時に毎回それをリセットすることです
kisna

16

あなたは働きすぎです。スクリプト全体は次のようになります。

if mkdir "$lockdir" 2> /dev/null; then 
  echo lock acquired
else
  echo could not acquire lock >&2
fi

しかし、それでもおそらく冗長すぎます。私はそれをコーディングします:

mkdir "$lockdir" || exit 1

しかし、結果のエラーメッセージは少しあいまいです。


1
欠けているエラーメッセージは、少し冗長ですが、簡単に修正できますmkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; };閉じ中かっこの前に注意してください)。また、オプションのメッセージパラメータを取得してstderrに出力し、リターンコード1で終了するfail関数を定義することもよくありますmkdir "$lockdir" || fail "could not create lock dir"
blubberdiblub 16

@blubberdiblub:fail関数は「現在の」関数またはスクリプトを終了できませんか?あなたがそれをしたいcmd || fail "error msg" || return 1なら、あなたはそれを使わなければならないでしょう、それはそうですか?
最大

@Maxは現在の関数ではありません。それは正しいことです。ただし、コマンドとして呼び出し、ソースを指定しなければ、現在のスクリプトを終了します。私は通常、そのようなfail機能は致命的な状況でのみ使用されると考えています。
blubberdiblub

12

それが真/偽のテストである場合は、return 0成功とreturn 1失敗の機能を備えています。テストは次のようになります。

if function_name; then
  do something
else
  error condition
fi

まさに私が探していたもの。
Samuel

この表記をパラメーター化された関数にも使用する方法はありますか?
Alex

@alexは、「パラメータ化された関数」が何を意味するのか、例を示すことができますか?
グレンジャックマン16

'myCopyFunc $ {SOURCE} $ {DEST}'、成功した場合は0を返します。この問題のように例:stackoverflow.com/questions/6212219/...
アレックス

はい、それはまったく問題あり
ません

2

私は失敗に対してsucc / 1に対して0を返す(glenn jackman)と思います。olibreの明確で説明的な答えがすべてを物語っています。結果がバイナリではなく、結果を「エコーアウト」するよりも変数を設定したい場合の一種の「コンボ」アプローチに言及するだけです(たとえば、関数が何かをエコーすることも想定している場合、このアプローチはうまくいかない)。それで?(以下はボーンシェルです)

# Syntax _w (wrapReturn)
# arg1 : method to wrap
# arg2 : variable to set
_w(){
eval $1
read $2 <<EOF
$?
EOF
eval $2=\$$2
}

のように(うん、例は多少ばかげています、それは単なる例です)

getDay(){
  d=`date '+%d'`
  [ $d -gt 255 ] && echo "Oh no a return value is 0-255!" && BAIL=0 # this will of course never happen, it's just to clarify the nature of returns
  return $d
}

dayzToSalary(){
  daysLeft=0
  if [ $1 -lt 26 ]; then 
      daysLeft=`expr 25 - $1`
  else
     lastDayInMonth=`date -d "`date +%Y%m01` +1 month -1 day" +%d`
     rest=`expr $lastDayInMonth - 25`
     daysLeft=`expr 25 + $rest`
  fi
  echo "Mate, it's another $daysLeft days.."
}

# main
_w getDay DAY # call getDay, save the result in the DAY variable
dayzToSalary $DAY

1

関数に渡すパラメーターがいくつかあり、その代わりに値が必要な場合。ここでは、関数の引数として「12345」を渡しています。処理した後、VALUEに割り当てられる変数XYZを返します。

#!/bin/bash
getValue()
{
    ABC=$1
    XYZ="something"$ABC
    echo $XYZ
}


VALUE=$( getValue "12345" )
echo $VALUE

出力:

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