在 bash 中,假设您有var=a.b.c.
,然后:
$ IFS=. printf "%s\n" $var
a.b.c
Run Code Online (Sandbox Code Playgroud)
但是,这样的用法IFS
在创建数组时确实生效:
$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c
Run Code Online (Sandbox Code Playgroud)
当然,这非常方便,但这是在哪里记录的?快速阅读Bash 文档中有关数组或分词的部分并没有给出任何指示。对于搜索IFS
通过单页文档不提供有关这种效应的任何暗示无论是。
我不确定何时可以可靠地执行以下操作:
IFS=x do something
Run Code Online (Sandbox Code Playgroud)
并预计这IFS
会影响场分裂。
Gil*_*il' 29
基本思想是VAR=VALUE some-command
set VAR
toVALUE
执行some-command
whensome-command
是一个外部命令,没有比这更花哨的了。如果您将这种直觉与有关 shell 工作原理的一些知识结合起来,在大多数情况下您应该会得出正确的答案。POSIX 参考是“Shell 命令语言”一章中的“简单命令”。
如果some-command
是外部命令,VAR=VALUE some-command
则相当于env VAR=VALUE some-command
. VAR
在 的环境中导出some-command
,并且它在 shell 中的值(或缺少值)不会改变。
如果some-command
是函数,则VAR=VALUE some-command
等价于VAR=VALUE; some-command
,即函数返回后赋值保持不变,变量不导出到环境中。其原因与 Bourne shell 的设计有关(以及随后的向后兼容性):它无法在函数执行期间保存和恢复变量值。不导出变量是有意义的,因为函数在 shell 本身中执行。但是,ksh(包括 ATT ksh93 和 pdksh/mksh)、bash 和 zsh 实现了更有用的行为,其中VAR
仅在函数执行期间设置(它也被导出)。在ksh 中,如果函数是用 ksh 语法定义的,则会完成此操作function NAME …
, 不是如果它是用标准语法定义的NAME ()
。在bash 中,这仅在 bash 模式下完成,而不是在 POSIX 模式下(使用 运行时POSIXLY_CORRECT=1
)。在zsh 中,如果posix_builtins
未设置该选项,则会执行此操作;默认情况下未设置此选项,而是通过emulate sh
或启用emulate ksh
。
如果some-command
是内置函数,则行为取决于内置函数的类型。特殊内置函数的行为类似于函数。特殊的内置函数必须在 shell 内部实现,因为它们会影响状态 shell(例如,break
影响控制流、cd
影响当前目录、set
影响位置参数和选项……)。其他内置函数只是为了性能和方便而内置的(主要是——例如 bash 功能printf -v
只能由内置函数实现),它们的行为就像一个外部命令。
赋值发生在别名扩展之后,所以如果some-command
是别名,首先扩展它以找出会发生什么。
请注意,在所有情况下,赋值都是在解析命令行之后执行的,包括命令行本身的任何变量替换。所以var=a; var=b echo $var
打印a
, 因为$var
在分配发生之前被评估。因此IFS=. printf "%s\n" $var
使用旧IFS
值拆分$var
.
我已经介绍了所有类型的命令,但还有一种情况:当没有要执行的命令时,即如果该命令仅包含赋值(可能还有重定向)。在这种情况下,分配仍然存在。VAR=VALUE OTHERVAR=OTHERVALUE
相当于VAR=VALUE; OTHERVAR=OTHERVALUE
。所以之后IFS=. arr=($var)
,IFS
仍然设置为.
。由于您可以$IFS
在赋值中使用toarr
并期望它已经具有新值,因此将 的新值IFS
用于 的扩展是有意义的$var
。
总之,您只能IFS
用于临时字段拆分:
third=$(IFS=.; set -f; set -- $var; echo "$3")
是一种复杂的做法,third=${var#*.*.}
除了当 的值var
包含少于两个.
字符时它们的行为不同);IFS=. some-function
wheresome-function
用 ksh 语法定义function some-function …
;IFS=. some-function
只要它们在本机模式而不是兼容模式下运行。小智 6
@Gilles 的回答真的很棒,他(详细)解释了一个复杂的问题。
但是,我相信为什么这个命令的答案:
$ IFS=. printf "%s\n" $var
a.b.c
Run Code Online (Sandbox Code Playgroud)
它的工作原理很简单,即在执行之前解析整个命令行。并且每个“单词”都由外壳处理一次。
的分配,象IFS=.
,被延迟(步骤4是最后一个):
4.- 每个变量分配应扩展...
直到执行命令之前,首先处理参数中的所有扩展以构建此可执行行:
$ IFS=. printf "%s\n" a.b.c ## IFS=. goes to the environment.
a.b.c
Run Code Online (Sandbox Code Playgroud)
的值$var
用“旧”IFS 扩展到a.b.c
命令printf
被赋予参数"%s\n"
和之前a.b.c
。
可以通过以下方式引入一级延迟eval
:
$ IFS=. eval printf "'%s\n'" \$var
a
b
c
Run Code Online (Sandbox Code Playgroud)
该行被解析(第一次)和'IFS =。' 设置为环境如下:
$ printf '%s\n' $var
Run Code Online (Sandbox Code Playgroud)
然后再次解析为:
$ printf '%s\n' a b c
Run Code Online (Sandbox Code Playgroud)
并执行到这个:
a
b
c
Run Code Online (Sandbox Code Playgroud)
的值$var
(ABC)是分裂与IFS的使用中的值:.
。
复杂和棘手的部分是什么在环境中有效 !!!
这在 Gilles 回答的第一部分中得到了很好的解释。
有一个额外的细节。
执行此命令时:
$ IFS=. arr=($var)
Run Code Online (Sandbox Code Playgroud)
IFS的值保留在目前的环境中,是的:
$ printf '<%s> ' "${arr[@]}" "$IFS"
<a> <b> <c> <.>
Run Code Online (Sandbox Code Playgroud)
但可以避免:为单个语句设置 IFS
$ IFS=. command eval arr\=\(\$var\)
$ printf '<%s> ' "${arr[@]}" "$IFS"
<a> <b> <c> <
>
Run Code Online (Sandbox Code Playgroud)