我试图弄清楚IFS如何影响bash中的单词拆分。该行为是取决于上下文的,其方式似乎与分词的直觉不匹配。
总体思路似乎很简单。从bash手册页引用:
例如,可以通过将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
(技巧在于用默认值评估 undefined variable NO_VAR。)
另一个类似的技巧,依赖于命令替换:
(IFS=,; for i in $(echo foo,bar,baz) ; do echo_n $i; done)
所以这是一个问题:控制IFS分词的上下文的推荐惯用方式是什么?
最佳答案
重要的是要认识到以下原因失败:
$ IFS=, echo_n foo,bar,baz
Num args: 1, Args: foo,bar,baz
对
IFS
的命令前分配仅适用于echo_n
内部; foo,bar,baz
不会在,
上拆分,因为此命令行上的任何单词拆分(或缺少单词拆分)都会在echo_n
运行之前发生。(IFS=,; for i in foo,bar,baz ; do echo_n $i; done)
因为
IFS
仅用于拆分扩展结果(并且通过read
,请参见下文),而不是文字字符串,所以会导致一次迭代。 shell 程序在第一次解析命令行时完成的单词拆分实际上是硬编码的,只能在空格上拆分。尚不清楚要完成什么,但是一个很好的经验法则是,如果您全局设置
IFS
的值,那么您所做的事情是错误的(或者至少是次优的)。在只有两种情况下,我可以记得有用地修改IFS
的情况:IFS=, read -r a b c
将包含逗号的行拆分为多个(此处为3个)。对IFS
的更改是对read
的本地更改;它读取的任何字符串都将完整读取,并且仅在内部由read
拆分。 foo=$(IFS=.; echo "${foo[*]}")
以.
作为定界符将数组的元素连接到单个字符串中。请注意,这是对IFS
的全局更改,但仅在全局范围内,该范围在命令替换完成后消失。 与
for
循环示例相关,每当您要遍历除硬编码列表(包括数组扩展)以外的内容时,就可能希望将while
循环与read
一起使用,而不是按照Bash FAQ 001进行for
循环。以此处的
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
关于bash - 如何在bash中控制IFS分词,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41813369/