私は意思決定構造について学んでおり、これらのコードに出会いました:
if [ -f ./myfile ]
then
cat ./myfile
else
cat /home/user/myfile
fi
[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile
両方とも同じように動作します。ある方法を他の方法から使用する利点はありますか?
私は意思決定構造について学んでおり、これらのコードに出会いました:
if [ -f ./myfile ]
then
cat ./myfile
else
cat /home/user/myfile
fi
[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile
両方とも同じように動作します。ある方法を他の方法から使用する利点はありますか?
回答:
いいえ、構造if A; then B; else C; fi
とA && B || C
は同等ではありません。
を使用するとif A; then B; else C; fi
、コマンドA
は常に評価されて実行され(少なくとも実行が試行されます)、コマンドB
またはコマンドのいずれかC
が評価されて実行されます。
ではA && B || C
、それはコマンドに対して同じだA
とB
で異なるが、C
:コマンドがC
あれば評価され、実行されるのいずれかが A
失敗したか B
失敗しました。
あなたの例ではchmod u-r ./myfile
、[ -f ./myfile ]
成功したにもかかわらず、cat /home/user/myfile
私のアドバイス:使用するA && B
かA || B
、あなたが望むすべて、これは読みやすく理解しやすいままであり、trapはありません。ただし、if ... then ... else ...の場合は、を使用しますif A; then B; else C; fi
。
ほとんどの人はif
... then
... else
... fi
フォームを理解する方が簡単だと感じています。
の場合、true a && b || c
をb
返すことを確認する必要があります。これは微妙なバグの原因であり、このスタイルを回避する正当な理由です。bがtrueを返さない場合、これらは同じではありません。
$ if true; then false ; else echo boom ; fi
$ true && false || echo boom
boom
else節のない非常に短いテストとアクションの場合、短縮された長さは魅力的です。例えば
die(){ printf "%s: %s\n" "$0" "$*" >&2 ; exit 1; }
[ "$#" -eq 2] || die "Needs 2 arguments, input and output"
if [ "$#" -ne 2 ] ; then
die "Needs 2 arguments, input and output"
fi
&&
そして、||
しているshort circuiting operators
とすぐに結果が知られているように、さらに不要なテストがスキップされ、。a && b || c
としてグループ化され(a && b) || c
ます。最初a
に実行されます。それは場合はfails
どのは0の終了ステータスを返していないと定義され、そのグループは(a && b)
に知られているfail
し、b
実行する必要はありません。||
式の結果は非常に実行する必要がわかりませんc
。a
成功した場合(ゼロを返す)、&&
オペレーターはその結果をまだ知らないa && b
ためb
、見つけるために実行する必要があります。場合はb
、その後成功a && b
成功し、||
全体的な結果が成功であるので、実行する必要はありません知っていますc
。もしb
その後、失敗しました||
式の値をまだ知らないので、実行する必要がありますc
。
演算子&&は、前のコマンドが正常に実行された場合に次のコマンドを実行します(終了コード($?)0 =論理値trueが返されます)。
フォームではA && B || C
、コマンド(または条件)Aが評価され、Aがtrue(成功、終了コード0)を返すと、コマンドBが実行されます。場合Aは(したがって返され失敗した偽 - 0以外の終了コード)および/またはBは、失敗した(戻り偽の)、コマンドCが実行されます。
また、&&
演算子は条件チェックでANDとして使用され、演算子は条件チェックでORの||
ように機能します。
スクリプトで何をしたいかに応じて、フォームA && B || C
を例のような条件チェックに使用したり、コマンドをチェーンしたり、前のコマンドが正常終了コード0であった場合に一連のコマンドを実行したりできます。
これが、次のようなコマンドを表示するのが一般的な理由です
do_something && do_something_else_that_depended_on_something
。
例:
apt-get update && apt-get upgrade
更新が失敗した場合、アップグレードは実行されません(実際には意味があります...)。
mkdir test && echo "Something" > test/file
この部分は、成功し、操作が終了コード0を返したecho "Something"
場合mkdir test
にのみ実行されます。
./configure --prefix=/usr && make && sudo make install
通常、必要な依存コマンドを一緒にチェーンするためにジョブをコンパイルする際に見られます。
あなたはとの「チェーン」の上に実装しようとした場合ならば - そして - 他の簡単な作業のために- (間違って行くことをより多くのもの、したがって、より多くのコードの書き込みに)あなたがはるかにコマンドやチェックが必要になります。
また、&&および||を使用したコマンドのチェーン化にも注意してください。シェルによって左から右に読み取られます。コマンドと条件チェックを角かっこでグループ化して、次のステップを前のコマンドの正常な出力に依存させる必要がある場合があります。たとえば、次を参照してください。
root@debian:$ true || true && false;echo $?
1
#read from left to right
#true OR true=true AND false = false = exit code 1=not success
root@debian:$ true || (true && false);echo $?
0
# true OR (true AND false)=true OR false = true = exit code 0 = success
または実際の例:
root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 ]] && [[ $c -eq 2 ]];echo $?
1
#condition $a = true OR condition b = true AND condition $c = false
#=> yields false as read from left to right, thus exit code=1 = not ok
root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 && $c -eq 2 ]];echo $?
0
#vars b and c are checked in a group which returns false,
#condition check of var a returns true, thus true OR false yields true = exit code 0
一部のコマンドは、実行されるプロセスに応じて異なる終了コードを返すか、アクションに応じて異なるコードを返すことに注意してください(たとえば、コマンドGNUはdiff
、2つのファイルが異なる場合は1を返し、そうでない場合は0を返します)。このようなコマンドは、&&および||で慎重に扱う必要があります。。
また、すべてのパズルをまとめるために、;
operator を使用したコマンドの連結に注意してください。formatを使用するとA;B;C
、command A
およびの終了コードが何であれ、すべてのコマンドが連続して実行されますB
。
これに関する混乱の多くは、これらのANDおよびORリストを呼び出すbashのドキュメントによるものです。論理的に類似している間&&
や||
角括弧の中に発見、彼らは異なる動作します。
いくつかの例がこれを最もよく示しているかもしれません...
注:単一および二重の角括弧(
[ ... ]
および[[ ... ]]
)は、比較を行って終了コードを返す独自のコマンドです。彼らは実際には必要ありませんif
。
cmda && cmdb || cmdc
場合はcmda
終了し、真の、cmdb
実行されます。falseで終了した
場合cmda
、cmdb
実行されませんが、実行されcmdc
ます。
cmda; cmdb && cmdc || cmdd
cmda
出口が無視される方法。
場合はcmdb
終了し、真の、cmdc
実行されます。falseで終了した
場合cmdb
、cmdc
実行されず、実行されcmdd
ます。
cmda && cmdb; cmdc
場合はcmda
、真の出口は、cmdb
実行され、続きますcmdc
。
場合はcmda
終了する偽の、cmdb
実行されませんが、cmdc
です。
え?なぜcmdc
実行されますか?
インタープリター;
にとって、セミコロン()と改行はまったく同じことを意味するためです。Bashはそのコード行を...
cmda && cmdb
cmdc
期待どおりの結果を得るには、cmdb; cmdc
中括弧で囲んで複合コマンド(グループコマンド)にする必要があります。追加の終了セミコロンは、{ ...; }
構文の要件にすぎません。それで…
cmda && { cmdb; cmdc; }
場合はcmda
、真の出口は、cmdb
実行され、続きますcmdc
。
もしcmda
終了し、偽のどちらcmdb
かcmdc
が実行されます。
実行は次の行から続行されます。
条件付きコマンドリストは、関数からできるだけ早く戻り、多くの不要なコードの解釈と実行を回避するのに最も役立ちます。ただし、複数の関数が返されるということは、関数を短くすることに執着しなければならないことを意味するため、考えられるすべての条件を確実にカバーできます。
実行中のコードの例を次に示します...
fnInit () {
:
_fn="$1"
### fnInit "${FUNCNAME}" ...
### first argument MUST be name of the calling function
#
[[ "$2" == "--help-all" ]] && { helpAll ; return 0; }
### pick from list of functions
#
[[ "$2" == "--note-all" ]] && { noteAll ; return 0; }
### pick from notes in METAFILE
#
[[ "$2" == "--version" ]] && { versionShow "${_fn}" "${@:3}"; return 0; }
#
[[ "$2" == "--function" ]] && {
isFnLoaded "$3" && { "${@:3}" ; return 0; }
#
errorShow functionnotfound "Unknown function: $3"
return 0
}
### call any loaded function
#
[[ "$2" == "--help" || "$2" == "-h" ]] && { noteShow "$_fn" "${@:3}"; return 0; }
### fnInit "${FUNCNAME}" --help or -h
#
return 1
}