web-dev-qa-db-ja.com

シェルスクリプトでwhileとdoの間

正常に動作する次のスクリプトがあります。

!/bin/bash

a=12
while [ $a -gt 10 ]
do
    echo "$a"
    a=$(($a-1))
done
echo "done"

「do」の上に「echo something」という行を追加すると、その行に構文エラーが表示されるはずです。のようだ [ $a -gt 10 ]はバイパスされ、無限ループになります。どうしてそうなるのでしょうか?

15
Geek

から bashマニュアル

while

whileコマンドの構文は次のとおりです。

whiletest-commands; doconsequent-commands; 
 done
consequent-commandsの終了ステータスがゼロである限り、test-commandsを実行します。戻りステータスは、consequent-commandsで最後に実行されたコマンドの終了ステータス、または何も実行されなかった場合はゼロです。

注:test-commandsplural。テストでは複数のコマンドを使用できるため、これは完全に有効なループであり、コマンドのリスト[ $a -gt 10 ]; echo "$a"をテストとして使用します。

while [ $a -gt 10 ]
echo "$a"
do
   a=$(($a-1))
done

コマンド[ $a -gt 10 ]は失敗する場合と失敗しない場合がありますが、echoは(ほとんど)常に成功します(テキストを書き込めなかったか、その他のエラーが発生した場合を除く)。したがって、finalテストコマンドの終了ステータスは常に成功し、ループは常に実行されます。

29
muru

man bashから:

一方、リスト-1;リスト2を実行します。終わった
whileコマンドはリストを継続的に実行しますlist-2
最後のコマンドがリストにある限りlist-1
ゼロの終了ステータスを返します。

これは、listに複数のコマンドが含まれている可能性があることを意味します(ほとんどがセミコロンまたは改行で区切られています)。

したがって、これは完全にうまく機能します:

#!/bin/bash

a=12

while 
    echo something
    echo "a before test =  $a"
    [ a -gt 10 ]
do
    echo "a after test =  $a"
    a=$(($a-1))
done
echo "done"

doの前の最後のコマンドがエコーである場合、do receiveが常に真(0)であるという終了コードがあり、ループは無限になります。

10
Isaac

ここで他の答えが意味するが明示的には言っていないのは、[は組み込みcommandであり、whileステートメントの構文部分ではありません。

入力してみてくださいhelp [コマンドラインで:

[[引数... ]
条件式を評価します。

これは組み込みの「テスト」の同義語ですが、最後の引数は
リテラルであること]、オープニングと一致する[

したがって、スクリプトはexactly as:

!/bin/bash
a=12
while test $a -gt 10
do
  echo "$a"
  a=$(($a-1))
done
echo "done"

どちらに変更するか:

!/bin/bash
a=12
while test $a -gt 10; echo something
do
  echo "$a"
  a=$(($a-1))
done
echo "done"
4
Paul Evans

これの極端な例として、/ usr/bin/tzselectは通常、メインステートメントのwhiledoの間に、caseステートメントとコマンド置換を含む約70行のコードがあります。 、およびdodoneの間の1行。

4
icarus

(他の回答を補足するメモとして)。

条件リストでいくつかのコマンドを使用することは、Cのdo { blah; blah; } while (condition)に似たループを実装するためによく使用されます。つまり、ループの最後で条件がチェックされ、ループ内のコードが少なくとも1回実行されます。

shでは、次のようにします。

while
  blah
  blah
  condition
do
  continue # or :
done

他のアプローチも可能ですが:

while true; do
  blah
  blah
  condition || break
done

または:

continue=true
while "$continue"; do
  blah
  blah
  condition || continue=false
done
end=false
until "$end"; do
  blah
  blah
  condition || end=true
done
2