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 ...; fi
Nor if; then ...; else ...; fi
Nor 1を許可しないことがわかりますif ...; then;else ...; fi
(しかし明らかにそうです)。導入されたので、POSIX仕様ベースのシェルは実際にブロックされます。if $empty; then $empty; else $empty; fi
zsh
sh
1実際に!
パイプの終了状態を無効にするキーワードを持たないBourneシェルでは、Bourneシェルは空のセクションを受け入れないため、セクションに明示的なnullコマンドを使用して代わりにif cmd; then :; else command if cmd fails; fi
作成する必要があります。if ! cmd; then command if cmd fails; fi
:
then
then
答え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ループを持つ他の多くの言語は、まったく同じように動作します。ループに提供する変数(またはリテラル)を明示的に要求しますが、これを実行するシェルスクリプトは、繰り返す項目のリテラルリストが定義される方法(つまり、空のリストは引用符なしの空の文字列です)のために存在しませんそうです。