exit
サブシェルでを実行することは1つの落とし穴です。
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
スクリプトは42を出力し、リターンコードでサブシェルを終了1
し、スクリプトを続行します。echo $(CALC) || exit 1
の戻りコードはの戻りコードにecho
関係なく0 であるため、呼び出しをに置き換えても役に立ちませんcalc
。そして、calc
前に実行されecho
ます。
さらに不可解な点は、次のスクリプトのように組み込み関数にexit
ラップすることで、その効果を妨げていlocal
ます。入力値を検証する関数を書いたとき、私はこの問題につまずいた。例:
「year month day.log」という名前の、つまり20141211.log
今日のファイルを作成したいと思います。日付は、妥当な値を提供できないユーザーによって入力されます。したがって、私の関数でfname
戻り値をチェックしてdate
、ユーザー入力の有効性を検証します。
#!/bin/bash
doit ()
{
local FNAME=$(fname "$1") || exit 1
touch "${FNAME}"
}
fname ()
{
date +"%Y%m%d.log" -d"$1" 2>/dev/null
if [ "$?" != 0 ] ; then
echo "fname reports \"Illegal Date\"" >&2
exit 1
fi
}
doit "$1"
いいね。スクリプトに名前を付けs.sh
ます。ユーザーがでスクリプトを呼び出すと./s.sh "Thu Dec 11 20:45:49 CET 2014"
、ファイル20141211.log
が作成されます。ただし、ユーザーがを入力./s.sh "Thu hec 11 20:45:49 CET 2014"
すると、スクリプトは次を出力します。
fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
この行fname…
は、サブシェルで不正な入力データが検出されたことを示しています。ただし、ディレクティブは常に返されるため、行exit 1
の最後はlocal …
トリガーされません。これは、後に実行されるため、戻りコードが上書きされるためです。そのため、スクリプトは続行され、空のパラメーターで呼び出されます。この例は単純ですが、bashの動作は実際のアプリケーションではかなり混乱する可能性があります。実際のプログラマーはローカルを使用しません。☺local
0
local
$(fname)
touch
明確にするために:なしではlocal
、無効な日付が入力されると、スクリプトは期待どおりに中断します。
修正は、次のように行を分割することです
local FNAME
FNAME=$(fname "$1") || exit 1
奇妙な動作local
は、bashのマニュアルページ内のドキュメントに準拠しています。「関数の外部でlocalを使用するか、無効な名前を指定するか、名前が読み取り専用変数でない限り、戻りステータスは0です。」
バグではありませんが、bashの動作は直観に反すると感じます。私は実行の順序を知っていますがlocal
、それでも壊れた割り当てを隠すべきではありません。
私の最初の答えには、いくつかの不正確さが含まれていました。mikeservとの明確で詳細な議論の後(ありがとう)、私はそれらを修正しに行きました。