POSIX forループ私が `for name do`(リストに単語なし)構文を許可するのはなぜですか?

POSIX forループ私が `for name do`(リストに単語なし)構文を許可するのはなぜですか?

forループの構文を理解しようとしています。POSIX シェル構文規則:

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

最後の項目(ほぼデフォルト)と最初の2つの項目はposixを引用して意味があります。

省略: in word... 次のようにする必要があります。 in "$@"

したがって、最初の2つはシェルスクリプトのパラメータを繰り返すことです。

forループがfor name in; do echo $name; done。私はまだシェルスクリプトのパラメータを繰り返すと思いましたが、小さなスクリプトを書くことはそうではありません。 forループが少し無視されているようです。では、第三の変形の目的は何ですか?

答え1

次のように定義しますwordlist

wordlist         : wordlist WORD
                 |          WORD

だから1つ以上しかしfor var in ...; do ...; done、ループは繰り返すことができます。0以上For name linebreak in sequential_sep do_groupだから、状況に対処する必要があります。単語0

wordlist~として定義された0個以上の単語:

wordlist         : wordlist WORD
                 | /* empty */

あまり混乱しないかもしれません。

見たらSUSv2、持っている:

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

これは、空のリストが許可されないことを意味します。したがって、SUSv3では、以下を追加して欠落を修正したようです。

For name linebreak in sequential_sep do_group

定義を変更する代わりにwordlist


ここではシェル言語の構文について話しているので、どこ/拡張すると空のリストが発生する可能性があるのではありませんfor i in $var; do ...; done。これはすべてシェル言語のPOSIXフォーマットのWORDタグです。for i in $(some cmd); do ...; done$var$(some cmd)$var$(some cmd)

私たちはい話しますが、for i in; do ...; done実際にはそうではありません言葉inと〜の間にあるdo

コードが特に役に立たないことに同意します。明示的に何も繰り返さないループを書く理由はありません。考えられるいくつかの理由は次のとおりです。

生成されたコード:

if ...; then
  list=' "foo" "bar baz" "$var" '
else
  list=
fi

eval '
  for i in '"$list"'; do
    printf "%s\n" "$i"
  done
'

あるいは、shスクリプトを生成し、for明示的なパラメータのループを構成するすべてです。

または、いくつかのコードをコメントアウトします。

for commented_out in; do
  this code is commented out
done

このような間:

:||:<<'EOF'
  this code is commented out and (harmless) even
  if it contains invalid syntax
EOF

もっと寛容になります。

標準を変更することになった変更要求は見つかりませんでしたが、主に既存のすべてのシェル実装が許可するため、変更したようなので標準で禁止するfor i in; do ...; done必要はありません。

BourneシェルとKornで始まるほとんどの実装(これは注目に値する例外です)のように、これを許可しない妥当な理由がないにもかかわらず、if then ...; else ...; fiNor if; then ...; else ...; fiNor 1を許可しないことがわかりますif ...; then;else ...; fi(しかし明らかにそうです)。導入されたので、POSIX仕様ベースのシェルは実際にブロックされます。if $empty; then $empty; else $empty; fizshsh


1実際に!パイプの終了状態を無効にするキーワードを持たないBourneシェルでは、Bourneシェルは空のセクションを受け入れないため、セクションに明示的なnullコマンドを使用して代わりにif cmd; then :; else command if cmd fails; fi作成する必要があります。if ! cmd; then command if cmd fails; fi:thenthen

答え2

たとえば、これらの動作の理由を説明するのが最善です。呼び出された変数があり、${items}その変数の各単語に対して関数を呼び出す必要がありますが、場合によっては空の文字列として評価できるとします。

POSIX 互換動作の場合は、次のようにします。

for x in ${items} ; do
   do_something(x)
done

ここでは、評価結果が空の文字列であるかどうかは重要ではありません${items}。空の文字列の場合、ループには繰り返す項目がなく、単にスキップされるからです。

ただし、シェルが空の単語リストの大文字と小文字を別々に処理する場合(まったく処理していない形式のいずれかと同じin)、次のものが必要です(少なくとも)。

if [ -n "${items}" ]; then
    for x in ${items} ; do
        do_something(x)
    done
fi

したがって、長さが不明な空の項目のリストを繰り返す必要がある比較的一般的な状況では、この動作は(少なくとも)ある程度のインデントと追加の条件チェックを節約します。アイテムのコレクション(PythonやJavaScriptなど)を繰り返すforループを持つ他の多くの言語は、まったく同じように動作します。ループに提供する変数(またはリテラル)を明示的に要求しますが、これを実行するシェルスクリプトは、繰り返す項目のリテラルリストが定義される方法(つまり、空のリストは引用符なしの空の文字列です)のために存在しませんそうです。

関連情報