bash shell - 临时 IFS 仅作为换行符。为什么这不起作用: IFS=$(echo -e '\n')
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19190967/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
shell - temp IFS as newline only. Why doesn't this work: IFS=$(echo -e '\n')
提问by user2672807
I'm trying to use for
in the shell to iterate over filenames with spaces. I read in a stackoverflow questionthat IFS=$'\n'
could be used and that seems to work fine. I then read in a comment to one of the answersthat to make it posix compatible for shells other than bash one could use IFS=$(echo -e '\n')
.
我试图for
在 shell 中使用来迭代带有空格的文件名。我读到了一个可以使用的stackoverflow 问题,IFS=$'\n'
这似乎工作正常。然后,我在评论中阅读了其中一个答案的评论,这些答案是为了使其与 bash 以外的其他 shell 兼容,可以使用IFS=$(echo -e '\n')
.
The former works but the latter doesn't. The comment is upvoted several times. I checked the output and the variable doesn't seem to contain the newline.
前者有效,但后者无效。这条评论被多次点赞。我检查了输出,变量似乎不包含换行符。
#!/bin/sh
IFS=$(echo -e '\n')
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
echo "Found $file"
done
echo
IFS=$'\n'
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
echo "Found $file"
done
The output shows the field separator is missing for the first:
输出显示第一个缺少字段分隔符:
0000000
Found one
two two
three
But is proper on the second:
但在第二个是正确的:
0000000 0a
0000001
Found one
Found two two
Found three
I found an answered question which may or may not be a duplicate of my question:
linux - When setting IFS to split on newlines, why is it necessary to include a backspace? - Stack Overflow
我发现了一个已回答的问题,该问题可能与我的问题重复,也可能不重复:
linux - 当将 IFS 设置为在换行符上拆分时,为什么需要包含退格键?- 堆栈溢出
Is there any danger to doing what's discussed there, IFS=$(echo -en '\n\b')
? Because that adds a \b
to IFS (I checked). Can a \b
occur in a file name? I don't want my code to split a file name by mistake.
做那里讨论的事情有什么危险IFS=$(echo -en '\n\b')
吗?因为这\b
为 IFS添加了一个(我检查过)。可以\b
出现在文件名中吗?我不希望我的代码错误地拆分文件名。
Also do you have any recommendations on how to properly handle restoration of IFS after the for
loop? Should I store it in a temporary variable, or run IFS and the for statement in a subshell? (and how to do that?) Thanks
另外,您对如何在for
循环后正确处理 IFS 的恢复有什么建议吗?我应该将它存储在一个临时变量中,还是在子 shell 中运行 IFS 和 for 语句?(以及如何做到这一点?)谢谢
采纳答案by Digital Trauma
Update- changing my pseudo-comment to a real answer.
更新- 将我的伪评论更改为真实答案。
I think this answershould explain the behavior you are seeing. Specifically command substitution operators $()
and backticks will strip trailing newlines from the command output. However the direct assignment in your second example doesn't do any command subsitution, so works as expected.
我认为这个答案应该解释你所看到的行为。特别是命令替换运算符$()
和反引号将从命令输出中去除尾随换行符。但是,第二个示例中的直接分配不执行任何命令替换,因此按预期工作。
So I'm afraid to say I think the upvoted comment you refer to is incorrect.
所以我不敢说我认为你提到的赞成的评论是不正确的。
I think the safest way to restore IFS is to set it in a subshell. All you need to do is put the relevant commands in parentheses ()
:
我认为恢复 IFS 最安全的方法是将其设置在子外壳中。您需要做的就是将相关命令放在括号中()
:
(
IFS=$'\n'
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
echo "Found $file"
done
)
Of course invoking a subshell incurs a small delay, so performance needs to be considered if this is to be repeated many times.
当然调用一个子shell会导致一个小的延迟,所以如果要多次重复,则需要考虑性能。
As an aside, be very careful, filenames can contain both \b and \n. I think just about the only characters they cannot contain are slash and \0. At least thats what it says on this wikipedia article.
顺便说一句,要非常小心,文件名可以同时包含\b 和\n。我认为它们不能包含的唯一字符是斜杠和 \0。至少这就是它在这篇维基百科文章中所说的。
$ touch $'12345\b67890'
$ touch "new
> line"
$ ls --show-control-chars
123467890 new
line
$
回答by Riccardo Galli
Since newlines are stripped by command substitution, as @DigitalTrauma correctly wrote, you should use a different, POSIX compliant way.
由于换行符被命令替换剥离,正如@DigitalTrauma 正确写的那样,您应该使用不同的、符合 POSIX 的方式。
IFS='
'
writes a newline and assign it to it. Simple and effective.
写一个换行符并将其分配给它。简单有效。
If you don't like assignments that span over two lines, you may use printf
as alternative.
如果您不喜欢跨越两行的作业,您可以使用printf
作为替代。
IFS="$(printf '\n')"