【问题标题】:How to control IFS word splitting in bash如何在 bash 中控制 IFS 分词
【发布时间】:2017-01-23 18:40:31
【问题描述】:

我试图弄清楚 IFS 如何影响 bash 中的分词。该行为与上下文相关,其方式似乎与分词的直觉不符。

总体思路似乎很简单。引用 bash 手册页:

shell 将 IFS 的每个字符视为分隔符,并将其他扩展的结果拆分为这些字符上的单词。 ...请注意,如果没有发生扩展,则不执行拆分。

这可以很容易地验证,例如通过将 IFS 变量设置为 ',' 并使用逗号分隔的参数列表调用 shell 函数。

echo_n () {
  echo Num args: $#, Args: "$@"
}
( IFS=','
  args=foo,bar,baz
  echo_n $args
)

正如预期的那样,这会导致 echo_n 出现三个不同的参数

Num args: 3, Args: foo bar baz

使用逗号分隔的列表直接调用 echo_n 失败,因为没有触发扩展。

IFS=, echo_n foo,bar,baz

结果

Num args: 1, Args: foo,bar,baz

到目前为止,事情似乎相当扭曲,但我可以绕着它们转。当我们开始在图片中添加 for 循环时,事情变得更加复杂。

(IFS=,; for i in foo,bar,baz ; do echo_n $i; done)

结果

Num args: 3, Args: foo bar baz

这违背了 for 循环的目的。

现在,我可以通过几个 bash 技巧中的任何一个来强制 IFS 分词,这些技巧可以强制触发某种形式的扩展。例如:

(IFS=,; for i in ${NO_VAR:-foo,bar,baz} ; do echo_n $i; done)

结果

Num args: 1, Args: foo
Num args: 1, Args: bar
Num args: 1, Args: baz

(诀窍在于使用默认值评估未定义的变量 NO_VAR。)

另一个类似的技巧,依赖于命令替换:

(IFS=,; for i in $(echo foo,bar,baz) ; do echo_n $i; done)

那么问题来了:什么是推荐的惯用方法来控制执行 IFS 分词的上下文?

【问题讨论】:

    标签: bash


    【解决方案1】:

    了解为什么以下失败很重要:

    $ IFS=, echo_n foo,bar,baz
    Num args: 1, Args: foo,bar,baz
    

    IFS 的预命令分配仅适用于内部 echo_nfoo,bar,baz 不会在 , 上拆分,因为此命令行上的任何分词(或缺少分词)都发生在 echo_n 运行之前。

    (IFS=,; for i in foo,bar,baz ; do echo_n $i; done)
    

    导致单次迭代,因为IFS 仅用于拆分扩展结果(以及read,见下文),而不是文字字符串。 shell 在第一次解析命令行时进行的分词实际上是硬编码的,只在空格上进行分词。


    您想要完成什么并不完全清楚,但一个好的经验法则是,如果您在全局范围内设置 IFS 的值,那么您做错了什么(或者至少是次优的)。我记得只有两种情况可以有效地修改IFS

    1. IFS=, read -r a b c 将包含逗号的行拆分为多个(此处为 3)部分。对IFS 的更改是read 本地的;它读取的任何字符串都被完整读取,并且仅在 内部read 分割。

    2. foo=$(IFS=.; echo "${foo[*]}"). 作为分隔符将数组元素连接成单个字符串。请注意,这是对 IFS 的全局更改,但仅在命令替换完成后消失的全局范围内。

    与您的for 循环示例相关,只要您想迭代硬编码列表以外的其他内容(包括数组的扩展),您可能希望使用while 循环和read而不是 for 循环,根据 Bash FAQ 001

    将您的for 循环放在这里,例如:

    (IFS=,; for i in $(echo foo,bar,baz) ; do echo_n $i; done)
    

    我会先将其拆分为一个数组,然后使用 for 进行迭代:

    data="foo,bar,baz"
    IFS=, read -r -a items <<< "$data"
    for i in "${data[@]}"; do
        echo_n "$i"
    done
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-27
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多