私は意思決定構造について学び、これらのコードに出くわしました:
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
は失敗しますorB
は失敗します。
あなたの例では、あなたがchmod u-r ./myfile
、それでも[ -f ./myfile ]
成功、あなたはcat /home/user/myfile
私のアドバイス:A && B
またはA || B
必要なものはすべて、これは読みやすく、理解しやすく、トラップはありません。しかし、もし…それなら…それ以外…なら、if A; then B; else C; fi
。
ほとんどの人は、if
... then
... else
... fi
フォームを理解する方が簡単だと感じます。
a && b || c
の場合、b
がtrueを返すことを確認する必要があります。これは微妙なバグの原因であり、このスタイルを回避する良い理由です。 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
が実行されます。終了ステータス0を返さないと定義されているfails
の場合、グループ(a && b)
はfail
に認識されており、b
を実行する必要はありません。 。 ||
は式の結果を認識しないため、c
を実行する必要があります。 a
が成功する(ゼロを返す)場合、&&
演算子はa && b
の結果をまだ認識していないため、b
を実行して調べる必要があります。 b
が成功した場合、a && b
は成功し、||
は全体的な結果が成功であることを認識しているため、c
を実行する必要はありません。 b
が失敗した場合でも、||
は式の値をまだ認識していないため、c
を実行する必要があります。
演算子&&は、前のコマンドが正常に実行された場合、次のコマンドを実行します(戻りコード($?)0 =論理true)。
A && B || C
の形式では、コマンド(または条件)[〜#〜] a [〜#〜]が評価され、if [〜#〜] a [〜#〜]はtrue(成功、終了コード0)を返し、次にコマンド[〜#〜] b [〜#〜]が実行されます。 [〜#〜] a [〜#〜]が失敗した場合(したがってfalse-0以外の終了コードが返されます)および/または[〜#〜 ] b [〜#〜]失敗(falseを返す)コマンド[〜#〜] c [〜#〜]が実行されます。
また、&&
演算子は条件チェックで[〜#〜] and [〜#〜]として使用され、演算子||
は[〜#〜]のように機能しますor [〜#〜]条件チェック。
スクリプトの処理内容に応じて、フォームA && B || C
を例のような条件チェックに使用したり、コマンドをチェーンして、前のコマンドが正常に終了コードを実行した場合に一連のコマンドを実行したりするために使用できます。 。
これが、次のようなコマンドがよく見られる理由です。do_something && do_something_else_that_depended_on_something
。
例:apt-get update && apt-get upgrade
更新が失敗した場合、アップグレードは実行されません(現実の世界では理にかなっています...)。
mkdir test && echo "Something" > test/file
echo "Something"
の部分は、mkdir test
が成功し、操作が終了コードを返した場合にのみ実行されます。
./configure --prefix=/usr && make && Sudo make install
通常、必要な依存コマンドを一緒にチェーンするジョブのコンパイルで見つかります。
上記の「チェーン」をif -then -elseで実装しようとすると、さらに多くのコマンドとチェックが必要になります(したがって、書く-うまくいかないこと).
また、&&およびを含むチェーンコマンドは、シェルによって左から右に読み取られることに注意してください。コマンドと条件チェックを角かっこでグループ化して、前のコマンドの正常な出力に次のステップを依存させる必要がある場合があります。例えばこれを見てください:
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
、returns 1 2つのファイルが異なる場合、および異なる場合)このようなコマンドは、&&およびで注意して扱う必要があります。。
また、すべてのパズルをまとめるために、;
演算子を使用したコマンドの連結に注意してください。形式A;B;C
では、コマンドA
およびB
の終了コードに関係なく、すべてのコマンドが連続して実行されます。
これに関する混乱の多くは、これらを呼び出すbashのドキュメントが原因である可能性があります ANDおよびORリスト 。論理的には&&
および||
は角括弧内にあり、機能が異なります。
いくつかの例がこれを最もよく示しています...
注:単一および二重の角括弧(
[ ... ]
および[[ ... ]]
)は、比較を行って終了コードを返す、それ自体のコマンドです。実際にはif
は必要ありません。
cmda && cmdb || cmdc
cmda
がtrueの場合、cmdb
が実行されます。cmda
がfalseで終了する場合、cmdb
は実行されませんが、cmdc
は実行されます。
cmda; cmdb && cmdc || cmdd
cmda
の終了方法は無視されます。cmdb
がtrueの場合、cmdc
が実行されます。cmdb
がfalseで終了する場合、cmdc
は実行されず、cmdd
は実行されます。
cmda && cmdb; cmdc
cmda
がtrueで終了する場合、cmdb
が実行され、その後にcmdc
が実行されます。cmda
がfalseで終了する場合、cmdb
は実行されませんがcmdc
は実行されます。
え? cmdc
が実行されるのはなぜですか?
インタープリターにとって、セミコロン(;
)と改行はまったく同じことを意味します。バッシュはそのコード行を...
cmda && cmdb
cmdc
期待どおりの結果を得るには、cmdb; cmdc
を中かっこで囲んで 複合コマンド(グループコマンド) にする必要があります。追加の終了セミコロンは、{ ...; }
構文の要件にすぎません。だから私たちは...
cmda && { cmdb; cmdc; }
cmda
がtrueの場合、cmdb
が実行され、その後にcmdc
。が続きますcmda
がfalseで終了する場合、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
}