是否有一种对变量中的单词或行进行“for”循环的语法,该语法可以在 bourne shell 和 zsh 中未经修改地工作?

是否有一种对变量中的单词或行进行“for”循环的语法,该语法可以在 bourne shell 和 zsh 中未经修改地工作?

我有一个变量,其中包含要在命令中循环的字符串列表,每行一个for...in...do...done

我经常在 bourne shell 和 zsh 之间切换。据我所知,默认情况下,zsh 不会在换行符或空格处从字符串中分割单词;伯恩壳确实如此。因此,像 zsh 这样的命令for list_item in $list; do...会失败,而它们可以在 bourne shell 中工作,或者使用文字文本而不是变量。我尝试在命令中使用IFS=并引用变量,但似乎无法取得进展。

是否有一个用于循环字符串中的单词/行的单一语法,可以在两个 shell 中不经修改地工作,以便轻松实现?如果不是,最佳实践是什么?

答案1

您可以通过使用 选项显式设置 zsh 的分词行为来与 bash 类似,从而在 bash 和 zsh 之间实现一致的行为shwordsplit。例如:

set -o shwordsplit

var='foo bar baz'
for item in $var; do
    echo "$item"
done

这将使 zsh 根据内部字段分隔符 ( IFS) 执行分词,就像 bash 一样。

答案2

Bourne shell 已不再使用,并且是 POSIX 之前的版本,并且不符合 POSIX 标准。有一些基于 Bourne shell 且符合 POSIX 标准的 shell:原始 Korn shell(在 ksh93 重写之前),因为 sh 的 POSIX 规范就是基于该 shell,而 bosh 基于 OpenSolaris sh(本身基于SysV shell 有一些修改,本身基于 Bourne shell)。

FreeBSDsh基于 Almquist shell(它本身是 SysV shell 的一个大多数兼容的开源克隆,具有一些扩展,包括一些来自 ksh 的扩展,例如$(...)),但经过修改以使其兼容 POSIX。

在 POSIX 中sh,要在循环中循环行for,不能使用 split+glob,因为换行符是空白字符,对于 IFS 分割一个或多个换行符的任何序列充当分隔符,因此类似:

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使用以下内容构建循环 shell 代码:

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
'

相关内容