if elseステートメントがlogicalおよび&&または||と同等 そして、どこよりもどちらを好むべきですか?


27

私は意思決定構造について学んでおり、これらのコードに出会いました:

if [ -f ./myfile ]
then
     cat ./myfile
else
     cat /home/user/myfile
fi


[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile

両方とも同じように動作します。ある方法を他の方法から使用する利点はありますか?



3
それら同等ではありません。イカロスの優れた答えをご覧ください。たとえば、。/ myfileが存在するが読み取りできない場合を考えます。
AlexP


回答:


26

いいえ、構造if A; then B; else C; fiA && B || C同等ではありません。

を使用するとif A; then B; else C; fi、コマンドAは常に評価されて実行され(少なくとも実行が試行されます)、コマンドBまたはコマンドのいずれかCが評価されて実行されます。

ではA && B || C、それはコマンドに対して同じだABで異なるが、C:コマンドがCあれば評価され、実行されるのいずれかが A失敗した B失敗しました。

あなたの例ではchmod u-r ./myfile[ -f ./myfile ]成功したにもかかわらず、cat /home/user/myfile

私のアドバイス:使用するA && BA || B、あなたが望むすべて、これは読みやすく理解しやすいままであり、trapはありません。ただし、if ... then ... else ...の場合は、を使用しますif A; then B; else C; fi


29

ほとんどの人はif... then... else... fiフォームを理解する方が簡単だと感じています。

の場合、true a && b || cb返すことを確認する必要があります。これは微妙なバグの原因であり、このスタイルを回避する正当な理由です。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実行する必要はありません。||式の結果は非常に実行する必要がわかりませんca成功した場合(ゼロを返す)、&&オペレーターはその結果をまだ知らないa && bためb、見つけるために実行する必要があります。場合はb、その後成功a && b成功し、||全体的な結果が成功であるので、実行する必要はありません知っていますc。もしbその後、失敗しました||式の値をまだ知らないので、実行する必要がありますc


7

演算子&&は、前のコマンドが正常に実行された場合に次のコマンドを実行します(終了コード($?)0 =論理値trueが返されます)。

フォームではA && B || C、コマンド(または条件)Aが評価され、Atrue(成功、終了コード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


1

これに関する混乱の多くは、これらのANDおよびORリストを呼び出すbashのドキュメントによるものです。論理的に類似している間&&||角括弧の中に発見、彼らは異なる動作します。

いくつかの例がこれを最もよく示しているかもしれません...

注:単一および二重の角括弧([ ... ]および[[ ... ]])は、比較を行って終了コードを返す独自のコマンドです。彼らは実際には必要ありませんif

cmda  && cmdb  || cmdc

場合はcmda終了し、真の、cmdb実行されます。falseで終了した
場合cmdacmdb実行されませんが、実行されcmdcます。

cmda; cmdb  && cmdc  || cmdd

cmda出口が無視される方法。
場合はcmdb終了し、真の、cmdc実行されます。falseで終了した
場合cmdbcmdc実行されず、実行されcmddます。

cmda  && cmdb; cmdc

場合はcmda、真の出口は、cmdb実行され、続きますcmdc
場合はcmda終了する偽の、cmdb実行されませんが、cmdcです。

え?なぜcmdc実行されますか?
インタープリター;にとって、セミコロン()と改行はまったく同じことを意味するためです。Bashはそのコード行を...

cmda  && cmdb
cmdc  

期待どおりの結果を得るには、cmdb; cmdc中括弧で囲んで複合コマンド(グループコマンド)にする必要があります。追加の終了セミコロンは、{ ...; }構文の要件にすぎません。それで…

cmda && { cmdb; cmdc; }
場合はcmda、真の出口は、cmdb実行され、続きますcmdc
もしcmda終了し、偽のどちらcmdbcmdcが実行されます。
実行は次の行から続行されます。

使用法

条件付きコマンドリストは、関数からできるだけ早く戻り、多くの不要なコードの解釈と実行を回避するのに最も役立ちます。ただし、複数の関数が返されるということは、関数を短くすることに執着しなければならないことを意味するため、考えられるすべての条件を確実にカバーできます。

実行中のコードの例を次に示します...

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