私は、変数代入を単純にコマンド置換で実行すると、コマンドがどのエラーコードを返すかについて混乱しています:
_a=$(false); echo $?
_
_1
_を出力します。これにより、変数の割り当てが最後のコードでスイープしたり新しいエラーコードを生成したりすることはありません。しかし、私がこれを試したとき:
_false; a=""; echo $?
_
それは_0
_を出力します。これは明らかに_a=""
_が返すものであり、false
によって返される_1
_をオーバーライドします。
これが起こる理由を知りたいのですが、変数の割り当てに他の通常のコマンドとは異なる特殊性はありますか?または、単にa=$(false)
が単一のコマンドであると見なされ、コマンド置換部分のみが理にかなっていますか?
-UPDATE-
みんなありがとう、答えとコメントから、「コマンド置換を使用して変数を割り当てると、終了ステータスはコマンドのステータスになります」というポイントを得ました。 (@Barmarによる)、この説明は非常に明確で理解しやすいですが、話すのはプログラマにとって十分に正確ではありません.TLDPまたはGNUなどの当局からこの点の参照を見たいです= manページ、見つけてくれてありがとう、ありがとう!
$(command)
としてコマンドを実行すると、 コマンドの出力自体を置き換える が許可されます。
あなたが言う時:
a=$(false) # false fails; the output of false is stored in the variable a
コマンドfalse
によって生成された出力は、変数a
に格納されます。さらに、終了コードはコマンドによって生成されたものと同じです。 help false
は次のことを示します。
false: false
Return an unsuccessful result.
Exit Status:
Always fails.
一方、言って:
$ false # Exit code: 1
$ a="" # Exit code: 0
$ echo $? # Prints 0
a
への割り当ての終了コード(0
)が返されます。
編集:
manual からの引用:
拡張の1つにコマンド置換が含まれていた場合、コマンドの終了ステータスは、最後に実行されたコマンド置換の終了ステータスです。
BASHFAQ/002 からの引用:
変数にコマンドの戻り値や出力を保存するにはどうすればよいですか?
...
output=$(command)
status=$?
output
への割り当ては、command
の終了ステータスに影響を与えません。終了ステータスは、まだ$?
にあります。
関数内でlocal
を使用する場合はそうではないことに注意してください。これは、受け入れられた回答に記載されている動作と、ここに投稿されたリンクとは微妙に異なる動作です。 http://mywiki.wooledge.org/BashFAQ/002
たとえば、次のbashスクリプトをご覧ください。
#!/bin/bash
function funWithLocalVar() {
local output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
function funWithoutLocalVar() {
output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
funWithLocalVar
funWithoutLocalVar
これの出力は次のとおりです。
nick.parry@nparry-laptop1:~$ ./tmp.sh
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1
たぶん誰も気にしないが、私はした。明らかにそうではないのに、ステータスコードが常に0だった理由を理解するのに1分かかりました。理由は100%明確ではありません。しかし、これを知っているだけで役に立ちました。
昨日(2018年8月29日)同じ問題に遭遇しました。
Nick P.の回答 で言及されたlocal
と、 受け入れられた回答 の@sevkoのコメントに加えて、グローバルスコープのdeclare
も同じ動作をします。
これが私のBashコードです。
#!/bin/bash
func1()
{
ls file_not_existed
local local_ret1=$?
echo "local_ret1=$local_ret1"
local local_var2=$(ls file_not_existed)
local local_ret2=$?
echo "local_ret2=$local_ret2"
local local_var3
local_var3=$(ls file_not_existed)
local local_ret3=$?
echo "local_ret3=$local_ret3"
}
func1
ls file_not_existed
global_ret1=$?
echo "global_ret1=$global_ret1"
declare global_var2=$(ls file_not_existed)
global_ret2=$?
echo "global_ret2=$global_ret2"
declare global_var3
global_var3=$(ls file_not_existed)
global_ret3=$?
echo "global_ret3=$global_ret3"
出力:
$ ./declare_local_command_substitution.sh 2>/dev/null
local_ret1=2
local_ret2=0
local_ret3=2
global_ret1=2
global_ret2=0
global_ret3=2
上記の出力のlocal_ret2
およびglobal_ret2
の値に注意してください。終了コードは、local
およびdeclare
によって上書きされます。
私のBashバージョン:
$ echo $BASH_VERSION
4.4.19(1)-release
(元の質問への回答ではなく、コメントするには長すぎます)
export A=$(false); echo $?
は0を出力することに注意してください!どうやら devnull's answer で引用されているルールは適用されなくなりました。その引用に少しコンテキストを追加するには(エンファシスマイニング):
3.7.1単純なコマンド拡張
...
展開後にコマンド名が残っている場合、実行は説明されているように進行しますbelow。 それ以外の場合、コマンドは終了します。拡張の1つにコマンド置換が含まれていた場合、コマンドの終了ステータスは、最後に実行されたコマンド置換の終了ステータスです。コマンドの置換がなかった場合、コマンドはステータス0で終了します。
3.7.2コマンドの検索と実行[—これは「以下」のケースです]
IIUCのマニュアルでは、var=foo
構文の特殊なケースとしてvar=foo command...
について説明しています(かなりわかりにくい!)。 「最後のコマンド置換の終了ステータス」ルールは、コマンドなしの場合にのみ適用されます。
export var=foo
を「変更された割り当て構文」と考えるのは魅力的ですが、そうではありません— export
は組み込みコマンドです(たまたま割り当てのような引数を取ります)。
=> varをエクスポートし、コマンド置換ステータスをキャプチャする場合は、2段階で実行します。
A=$(false)
# ... check $?
export A
この方法はset -e
モードでも機能します。コマンド置換が0以外を返すとすぐに終了します。