Bourne Shellとzshで変更されずに動作する変数の単語や行を繰り返す「for」構文はありますか?

Bourne Shellとzshで変更されずに動作する変数の単語や行を繰り返す「for」構文はありますか?

コマンドでは、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
'

関連情報