web-dev-qa-db-ja.com

論理および&&または||と同等のelseステートメントそしてどこで私はどちらを好むべきですか?

私は意思決定構造について学び、これらのコードに出くわしました:

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


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

どちらも同じように動作します。ある方法を別の方法で使用する利点はありますか?

27
Subhaa Chandar

いいえ、構造if A; then B; else C; fiおよびA && B || C同等ではないです。

if A; then B; else C; fi、コマンドAは常に評価および実行され(少なくともそれを実行する試みが行われます)、次にコマンドBまたはコマンドCが評価および実行されます。

A && B || C、それはコマンドABで同じですが、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

26
xhienne

ほとんどの人は、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を実行する必要があります。

29
icarus

演算子&&は、前のコマンドが正常に実行された場合、次のコマンドを実行します(戻りコード($?)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の終了コードに関係なく、すべてのコマンドが連続して実行されます。

7
George Vasiliou

これに関する混乱の多くは、これらを呼び出す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で終了する場合、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
}
1
DocSalvager