在 zsh 中循环以空格分隔的字符串

什么控制环境知道在 zsh 中按空间分割?

我敢肯定这很简单,但在我所有的搜索中还没有弄清楚是什么控制了它。

尝试遍历以空格分隔的字符串中的项目,如下所示:

s='foo bar baz'
for i in $s; do
  echo "$i END"
done
# foo bar baz END

# ---

s='foo bar baz'
a=( $s )
echo ${a[0]} # (empty)
echo ${a[1]} # foo bar baz

# ---

s='foo bar baz'
IFS=' ' read a <<< $s
for i in "${a[@]}"; do
  echo "$i END"
done
# foo bar baz END

不同的方法通过 shbash 工作,但在带有 oh-my-zsh 的 shell 中,我无法用空格分隔,得到上面的结果。可能不是 oh-my-zsh - 但希望了解是什么推动了这一点。

来自 bash 的工作示例:

s='foo bar baz'
for i in $s; do
  echo "$i END"
done
# foo END
# bar END
# baz END
stack overflow Loop over space-separated string in zsh
原文答案

接受的答案

Zsh 和 bash 是两种不同的编程语言。它们相似,但不完全相同。在 bash 中,更普遍的是在 Bourne 风格的 shell( shdashksh, …)中,不带引号的变量扩展 $foo 执行以下操作:

1.取变量 foo 的值,是一个字符串。 (如果没有变量 foo ,则取空字符串。)

  1. 将字符串拆分为以空格分隔的部分。 (更一般地说, IFS 变量的值决定了字符串的分割方式;我不会在这里详述所有细节。)结果是一个字符串列表。
  2. 对于列表中的每个元素,如果它是一个通配符模式,即如果它包含至少一个通配符 *?[ (并且可能更多取决于一些 shell 选项),并且该模式至少匹配一个文件名,然后该元素被匹配文件名列表替换。不包含任何通配符的元素,以及包含通配符但不匹配任何文件名的元素将被保留。结果又是一个字符串列表。

Zsh 主要是 Bourne 风格的 shell,但它有一些差异,这是主要的差异: $foo 具有以下更简单的行为。

1.取变量 foo 的值,是一个字符串。 (如果没有变量 foo ,则取空字符串。)

  1. 如果这导致空词,则消除该词。 (因此,例如 $foo$bar 仅在 foobar 均为空或未设置时才被消除。)

请注意,在 sh 或 bash 中, $foo 仅在字符串不包含任何通配符或使用 set -f 禁用通配符时才用于拆分字符串。

要在 zsh 中以空格分割字符串,有两种简单的方法:

这与 oh-my-zsh 无关,oh-my-zsh 是一个配置 zsh 以供交互使用的插件。


答案:

作者头像

只是 ${(p: :)foo} 对我不起作用,并且给出了 zsh: error in flags 错误。阅读 Parameter-Expansion 文档后,我看到它应该是 ${(ps: :)foo} 。甚至文档中的标志 p 解释也使用了额外的 s 标志。

p 标志文档说:

p : 将相同的转义序列识别为 print 内置字符串参数到下面描述的任何标志 跟随此参数

所以我最终使用的只是 ${(s: :)foo} 。请参阅行为示例,其中仅 space 用作分隔符,连续空格被视为一个,而 tabsnewlines 保留原样:


> FRUITS="appletbanana grapes orange      passion_fruitnwatermelon"

> for F in ${(ps: :)FRUITS}; do echo "GOT: <$F>"; done
GOT: <apple banana>
GOT: <grapes>
GOT: <orange>
GOT: <passion_fruit
watermelon>