shループの「do」の後に「;」がないのはなぜですか?


28

単一行で記述されたときにシェルループ内に;文字がないのはなぜdoですか?

ここに私が言いたいことがあります。複数行で記述された場合、forループは次のようになります。

$ for i in $(jot 2)
> do
>     echo $i
> done

そして、1行で:

$ for i in $(jot 2); do echo $i; done

すべての倒壊ラインが得る;それらの後を除くためのdoライン、そしてあなたが含まれている場合;、それは誤りです。おそらく、これが理由で正しいことであると私が判断したよりもはるかに賢明な誰かが、おそらくその理由がわからない。私には矛盾しているようです。

whileループでも同じです。

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
一貫性があります:while/の後にセミコロンはありませんfor
ドミトリーグリゴリエフ

すぐに頭に浮かぶのは、それが必要なわけではないということです。他の場所では、セミコロンがステートメントを区別します。
ポールドレーパー

冗長だからです。それなしではあいまいさはありません。
user207421

回答:


29

これがコマンドの構文です。複合コマンドを参照

for name [ [in [words …] ] ; ] do commands; done

特に注意してください: do commands

ほとんどの人は読みやすくするためにdocommandsを別の行に配置しますが、必ずしもそうする必要はありません。

for i in thing
do something
done

この質問は特にシェルに関するものであり、bashマニュアルにリンクしていることは知っています。シェルのマニュアルにはそのように書かれていませんが、バイトマガジンのためにスティーブン・ボーン書い記事にはそのように書かれています。

スティーブンは言う:

コマンドリストは、改行などにより分離または終了1つ以上の単純なコマンドのシーケンスである;(セミコロン)。さらに、などの予約語やる行っては通常、改行が先行したりしている;...には、次のコマンドリストたびに回しDOが実行されます。


5
のように、セミコロンがあってはならない理由も興味深いでしょうfor i in thing; do; something; done。改行は許可されますが、セミコロンは許可されません。
レックスコギタン

@gidds:はい、正確に。それがシェル文法の構造です。これsomethingが主題でありdo、それに適用されます。英語の文構造のように。
ピーターコーデス

@giddsリテラル(または他の構文)が必要なのは、複数のコマンドが条件(while構文)に入ることができるため、条件コマンドをループ本体コマンドと区別するための何かが必要だからです。doそれらを分離するために使用することは、おそらく最もふさわしいと思われるものです。
JoL

@rexkogitans実際、私はそれが構文エラーだとは思いもしませんでした。このタイトルの質問も、それにぴったりです。おそらく、空のボディを許可しないことに関係しています。ただし、改行を使用して空の本文を作成すると、わずかに異なる構文エラーが発生します。例には空の本文はありません。
JoL

@rexkogitansここのセミコロンはループ構文の一部であり、コマンドリストターミネーターとしての他の役割とは異なります。改行は、そうでない場合に必要または予期されない場合、通常の空白のように扱われるため、異なります。シェルの文法は面倒です。
チェプナー

12

この構文の元の理由がわからないがwhile、条件セクションでループが複数のコマンドを取ることができるという事実を考えてみましょう。

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

ここで、doキーワードは条件セクションとループの本体を区別するために必要です。dowhileifおよびthen(およびdone)のようなキーワードであり、セミコロンや改行を必要とせず、コマンドの直前に表示されることは他の行と一致しているようです。

代替(ifおよびに一般化while)はややいように見えますが、次のように記述する必要があります。

if; [ -n "$a" ]; then
    some command here
fi

forループの構文は似ています。


11

シェル構文はプレフィックスベースです。特別なキーワードによって導入された句があります。特定の節は一緒に行かなければなりません。

whileループは、一つ以上のテストコマンドから構成されています。

test ; test ; test ; ...

そして、1つ以上のbodyコマンドにより:

body ; body ; body ; ...

whileループが始まることをシェルに伝える必要があります。それがwhile言葉の目的です:

while test ; test ; test ; ...

しかし、その後、物事はあいまいです。どのコマンドが本文の始まりですか?何かがそれを示さなければならず、それがdo接頭辞の役割です:

do body ; body ; body ; ...

そして最後に、何かは最後の体が見られたことを示さなければなりません。特別なキーワード doneがそれを行います。

これらのシェルキーワードは、同じ行であってもセミコロンで区切る必要はありません。たとえば、複数のネストされたループを閉じる場合、単にを使用できますdone done done ...

むしろ、... test ; body ... 同じ行にある場合はセミコロンが間にあります。そのセミコロンはターミネータであると理解されています:それはに属しtestます。したがって、doそれらの間にキーワードを挿入する場合、セミコロンとの間で入力する必要がありbodyます。セミコロンの反対側にある場合、testコマンドの間に配置されるのではなく、コマンドの構文内に誤って埋め込まれます。

シェル構文は元々Stephen Bourneによって設計され、Algolに触発されています。BourneはAlgolを非常に愛していたため、シェルのソースコードで多くのCマクロを使用して、CをAlgolのように見せました。1979年の日付のシェルソースは、バージョン7 Unixから参照できます。マクロはにありmac.h、あちこちで使用されています。たとえば、ifステートメントはIF... ELSE... ELIF... としてレンダリングされますFI


ソースコードは印象的です。
keithpjolley

ええ、それはcppの力強いツールです。
盗難

7

doここでは特に特別なことではないと主張します。;コマンドリストを開始できないため、エラーが発生します。もしそうなら、最初のコマンドは空になり、文法は空のコマンドを許可しません(コマンドなしの変数の割り当てまたはリダイレクト、はい、しかし絶対に何も、いいえ)。これは、コマンドのリストが予想されるあらゆる場所で当てはまります。

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

でも:

$ ; ;
dash: 1: Syntax error: ";" unexpected

これらのすべての場所で、コマンドリストが予期されるため、先頭;が予期されません。


ええ-「do」の後に任意に「\ n」を追加した方法は、「do」はスタンドアロンのトークンであり、後続のブロックで動作するものではないと考えさせました。
keithpjolley

ただし、エスケープされていない改行(シェル構文ではセミコロンのように振る舞うように見える)do(およびifなど)の後に許可されますか?
ヘニングマクホルム

@Henningコマンドリストの前(および後)に任意の数の改行を使用できます。改行とセミコロンは、他の面でも異なる動作をします。たとえば、エイリアスの展開は、コマンドごとではなく、入力の行全体(つまり、次の改行まで)が読み取られたときに行われます。
muru

3

構文は次のとおりです。

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

コマンドリスト内、または「do」または「done」の前にある改行は、「;」に置き換えることができます。

物事をより見やすくするために、「do」の後と「in」の前に改行(「;」は不可)を使用できますが、改行を必要とすることは意味がありません。


3

if キーワードと似ています:コマンドが短い場合、これは非常に読みやすくなります:

if   test_commands
then true_commands
else false_commands
fi

理にかなった\n後、私は任意を入れていることに気づいたdo(他のコマンドと引数の間に任意を入れられないことを考えない限り\n)。
keithpjolley

まあ、dothenelseされていない引数 -彼らがいた場合、あなたは彼らの前にセミコロンを使用することを許可されません。これらはシェルキーワードです(参照type if then elif else fi
グレンジャックマン

返事をしたかったほど明確ではなかったと思います。
keithpjolley

私のポイントは、「do」は「他のコマンド」のようではないということでした。したがって、異なるルールに従います。
グレンジャックマン

3

これはPOSIXシェルの文法で指定されているためです。との間doにあるものdoneはすべてステートメントのグループと見なされますが、;シーケンシャルセパレーターは:

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

さらに、;必要な場合、標準は明示的にそう述べ、sequential_sepafter を含めますDo


1
@drewbenn一番最初のものですか?それは位置パラメータを反復処理しています。./myscript.sh abcのようなスクリプトがあり、abcが引数である場合、iのループです。echo "$ i"; doneはabcをループしますが、それが機能するにはセミコロンが必要だと思いますが
Sergiy Kolodyazhnyy


1

doはキーワードであり、それに続くものは基本的にパラメーターであるためです。Bashインタープリターは、さらに続くことを知っています。「;」がない方法に似ています。forコマンドのiに続きます。実際にiの後に改行を追加し、残りを新しい行に入力すると、正常に機能します。

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