为什么不能为一次printf设置IFS [英] Why can't I set IFS for one call of printf

查看:91
本文介绍了为什么不能为一次printf设置IFS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码行,其中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的环境变量列表中,并将字符串arg1arg2arg3制成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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆