コマンドでは、1行に1つずつ繰り返したい文字列のリストを含む変数がありますfor...in...do...done
。
Bourne Shellとzshを頻繁に切り替えます。私が知っている限り、zshはデフォルトでBernシェルから改行やスペースで文字列から単語を分離しません。したがって、zshなどのコマンドfor list_item in $list; do...
は失敗しますが、bourneシェルでは機能するか、変数の代わりにリテラルテキストを使用できます。コマンドで変数を使用して参照しようとしましたが、IFS=
進歩がないようです。
簡単に実装できるように、両方のシェルで変更されていない状態で動作する文字列の単語/行を繰り返す単一の構文はありますか?そうでない場合、ベストプラクティスは何ですか?
答え1
optionsを使用してbashと同様にzshのトークン化動作を明示的に設定すると、bashとzshの間の一貫した動作が得られますshwordsplit
。たとえば、
set -o shwordsplit
var='foo bar baz'
for item in $var; do
echo "$item"
done
IFS
これにより、zshはbashと同様に内部フィールド区切り文字()に基づいてトークン化を実行します。
答え2
Bourne シェルは廃止され、POSIX 以前のバージョンであり、POSIX と互換性がありません。 Bourne シェルに基づいたいくつかの POSIX 互換シェルがあります。元のKornシェル(ksh93書き換え前)(shのPOSIX仕様はそのシェルに基づいています)とboshはOpenSolaris sh
(それ自体はSysVシェルに基づいています)に基づいています。 Bourneシェルに基づくいくつかの修正)。
FreeBSDsh
はAlmquistシェル(それ自体はSysVシェルのほとんど互換性のあるオープンソースレプリカであり、kshなどのいくつかの拡張機能を含む$(...)
)に基づいていますが、POSIXと互換性があるように修正されました。
POSIXでは、sh
ループ内の行を繰り返すためにfor
分割+globを使用することはできません。改行は、IFSが1つ以上の改行シーケンスを分割する区切り文字として機能する空白文字であるため、次のようになります。
IFS='
' # split on newline
set -o noglob # disable the glob part
for line in $multiline_string; do
printf '<%s>\n' "$line"
don
空行はスキップされます。さらに、zsh
引用符を持たないパラメータ拡張は分割されたりワイルドカードとしてマークされたりしないため、その場所に配置する必要がありますemulate sh
。
"$@"
次の行を使用して配列を構成できます。$multiline_string
NL='
'
set --
while [ -n "$multiline_string" ]; do
line=${multiline_string%%"$NL"*}
set -- "$@" "$line"
multiline_string=${multiline_string#"$line"}
multiline_string=${multiline_string#"$NL"}
done
for line do
printf '<%s>\n'
done
または:
printf '%s\n' "$multiline_string" | {
set --
while IFS= read -r line; do
set -- "$@" "$line"
done
for line do
printf '<%s>\n' "$line"
done
}
for
または、以下を使用して繰り返しシェルコードを作成できます。
eval '
for line in '"$(
awk -v q="'" -F '\n' -v ORS=' ' -- '
BEGIN {
n = split(ARGV[1], line)
for (i = 1; i < n; i++) {
gsub(q, "&\\\\&&", line[i])
print q line[i] q
}
}' "$multiline_string")"'; do
printf "<%s>\n" "$line"
done
'