为什么不能为一次printf设置IFS [英] Why can't I set IFS for one call of printf
问题描述
考虑以下代码行,其中deps包含依赖项列表:
IFS=',' printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
对于一次调用printf,我将IFS
设置为,
,但奇怪的是printf
似乎不尊重IFS
,因为它没有将deps
扩展为逗号分隔的列表./p>
另一方面,如果我这样设置IFS:
IFS=','
printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
printf可以将deps正确地扩展到以逗号分隔的列表.
我在这里想念什么吗?
在此命令行中:
IFS=',' printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
printf
不会展开"${deps[*]}"
.外壳进行扩展.实际上,这总是很正确的.尽管printf
恰好是内置的shell,但它对其参数并没有做任何特殊的事情,并且使用外部printf
时,您将获得完全相同的行为.
语法
envvar=value program arg1 arg2 arg3
使外壳程序将envvar=value
添加到提供给program
的环境变量列表中,并将字符串arg1
,arg2
和arg3
制成program
的argv列表.在这一切发生之前,shell会正常进行各种类型的扩展,这将导致value
中引用的shell变量和三个参数被其值替换.但是环境变量设置envvar=value
并不是外壳程序执行环境的一部分.
同样,
FOO=World echo "Hello, $FOO"
在echo
的参数中扩展$FOO
时,
将不使用FOO=World
.在外壳的执行环境中,外壳将"Hello, $FOO"
进行扩展,然后将其作为参数传递给echo
,并将其作为环境的一部分传递给echo
.
将变量设置放在单独的命令中是完全不同的.
IFS=','; printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
在shell解析printf
命令之前,首先在 shell的环境中设置IFS
的值.然后,当外壳程序在参数中进行扩展时,它将最终传递给printf
,它将使用IFS
的值来扩展数组deps[*]
.在这种情况下,除非传递给printf
的环境变量中不包含IFS
,否则,除非先前已导出IFS
.
将IFS
与内置的read
一起使用可能会造成混淆,但与上面的内容完全一致.在命令中
IFS=, read A B C
IFS=,
作为环境变量列表的一部分传递给read
. read
消耗一行输入,并在其环境中查询IFS
的值,以便弄清楚如何将输入行拆分为单词.
为了出于自变量中参数扩展的目的而更改IFS
,必须在外壳环境中进行此更改,这是全局更改.由于您很少希望全局更改IFS
的值,因此常见的习惯用法是在使用()
创建的子外壳中进行更改:
( IFS=,; printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"; )
可能会做您想要的.
Consider this line of code where deps contains a list of dependencies:
IFS=',' printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
I set IFS
to be ,
for a single invocation of printf but strangely printf
doesn't seem to respect IFS
as it doesn't expand deps
to a comma-separated list.
On the other hand, if I set IFS like so:
IFS=','
printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
printf correctly expands deps to a comma-separated list.
Do I miss something here?
In this command line:
IFS=',' printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
printf
does not expand "${deps[*]}"
. The shell does the expansion. In fact, that's pretty well always true. Although printf
happens to be a shell builtin, it doesn't do anything special to its arguments, and you would get exactly the same behaviour with an external printf
.
The syntax
envvar=value program arg1 arg2 arg3
causes the shell to add envvar=value
to the list of environment variables provided to program
, and the strings arg1
, arg2
, and arg3
to be made into an argv list for program
. Before all that happens, the shell does its normal expansions of various types, which will cause shell variables referenced in the value
and the three arguments to be replaced with their values. But the environment variable setting envvar=value
is not part of the shell's execution environment.
Equally,
FOO=World echo "Hello, $FOO"
will not use FOO=World
when expanding $FOO
in the argument to echo
. "Hello, $FOO"
is expanded by the shell in the shell's execution environment, and then passed to echo
as an argument, and FOO=World
is passed to echo
as part of its environment.
Putting the variable setting in a separate command is completely different.
IFS=','; printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"
first sets the value of IFS
in the shell's environment, before the shell parses the printf
command. When the shell then does its expansions in the arguments it will eventually pass to printf
, it uses the value of IFS
in order to expand the array deps[*]
. In this case, IFS
is not included in the environment variables passed to printf
, unless IFS
has previously been exported.
The use of IFS
with the read
builtin may seem confusing, but it is entirely consistent with the above. In the command
IFS=, read A B C
IFS=,
is passed as part of the list of environment variables to read
. read
the consumes a line of input, and consults the value of IFS
in its environment in order to figure out how to split the input line into words.
In order to change IFS
for the purposes of parameter expansion in an argument, the change must be made in the shell's environment, which is a global change. Since you rarely want to globally change the value of IFS
, a common idiom is to change it within a subshell created with ()
:
( IFS=,; printf "setup-x86.exe -q -p='%s'\n" "${deps[*]}"; )
probably does what you want.
这篇关于为什么不能为一次printf设置IFS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!