我试图弄清楚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分词的上下文的推荐的惯用方法是什么?
认识到这一点很重要,为什么下面的失败:
$ 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[*]}")
将数组的元素连接到单个字符串中,并使用a .
作为分隔符.请注意,这是全局更改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