如何正确运行一些更改了IFS
变量值的命令(以更改字段拆分的工作方式和"$*"
处理方式),然后恢复 的原始值IFS
?
我知道我能做到
(
IFS='my value here'
my-commands here
)
Run Code Online (Sandbox Code Playgroud)
将 的更改本地化IFS
到子外壳,但我真的不想启动子外壳,尤其是如果我需要更改或设置需要在子外壳之外可见的变量的值时更是如此。
我知道我可以使用
saved_IFS=$IFS; IFS='my value here'
my-commands here
IFS=$saved_IFS
Run Code Online (Sandbox Code Playgroud)
但IFS
在原始IFS
实际上未设置的情况下,这似乎无法正确恢复。
寻找与 shell 无关(但 POSIX)的答案。
澄清:上面的最后一行意味着我对bash
-exclusive 解决方案不感兴趣。事实上,我使用最多的系统 OpenBSDbash
在默认情况下甚至根本没有安装,而且bash
它不是我用来回答本网站问题以外的任何其他事情的外壳。看到我可以在bash
或其他类似 POSIX 的 shell 中使用的解决方案更有趣,而无需努力编写不可移植的代码。
Kus*_*nda 26
是的,在 whenIFS
未设置的情况下,恢复值$saved_IFS
实际上会将值设置为IFS
(为空值)。
这会影响未加引号扩展的字段拆分方式,它会影响read
内置实用程序的字段拆分,并且会影响使用"$*"
.
如果未设置, IFS
这些事情会发生,好像 IFS
具有空格、制表符和换行符的值,但如果值为空,则不会进行字段拆分,并且位置参数将连接成没有分隔符的字符串当使用"$*"
. 所以,有区别。
要正确恢复IFS
,请考虑saved_IFS
仅在IFS
实际设置为某些内容时进行设置。
unset saved_IFS
[ -n "${IFS+set}" ] && saved_IFS=$IFS
Run Code Online (Sandbox Code Playgroud)
参数替换仅在设置时${IFS+set}
扩展为字符串,即使它设置为空字符串。如果未设置,则扩展为空字符串,这意味着测试将为假并保持未设置状态。set
IFS
IFS
-n
saved_IFS
现在,saved_IFS
如果IFS
最初未设置,则未设置,或者它具有具有的值,IFS
您可以设置所需的任何值IFS
并运行您的代码。
恢复时IFS
,您会做类似的事情:
unset IFS
[ -n "${saved_IFS+set}" ] && { IFS=$saved_IFS; unset saved_IFS; }
Run Code Online (Sandbox Code Playgroud)
finalunset saved_IFS
并不是真正必要的,但从环境中清除旧变量可能会很好。
LL3 在评论(现已删除)中建议的另一种方法是在unset
命令前面加上前缀:
,这是一个什么都不做的内置实用程序,在不需要时有效地注释掉unset
:
saved_IFS=$IFS
${IFS+':'} unset saved_IFS
Run Code Online (Sandbox Code Playgroud)
这将设置saved_IFS
为 的值$IFS
,但如果IFS
未设置,则将其取消设置。
然后设置IFS
为您的值并运行您的命令。然后用
IFS=$saved_IFS
${saved_IFS+':'} unset IFS
Run Code Online (Sandbox Code Playgroud)
(unset saved_IFS
如果您也想清理该变量,则可能紧随其后)。
请注意,:
必须像上面一样引用或转义为\:
,这样它就不会被$IFS
包含修改:
(毕竟,未引用的参数替换会调用字段拆分)。
在bash
函数内部,您可以使用local IFS=$'\n'
或 任何东西来隐藏该函数范围内的 while的全局(或父函数local
)值IFS
。进一步分配到IFS
仍将修改您的本地版本。
在bash
,
local
不在函数内时使用是错误的。
因此,如果您不编写函数或使用不带local
(或等效物)的 shell,这将无济于事,但是如果您是(并且您知道IFS
在所有点都想要的值,直到它返回),有一个简单而好的方法解决方案。
只要您使用
foo(){ ...; }
而不是foo() ( ... )
.