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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 08:11:00  来源:igfitidea点击:

shell - temp IFS as newline only. Why doesn't this work: IFS=$(echo -e '\n')

bashshellifs

提问by user2672807

I'm trying to use forin 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 \bto IFS (I checked). Can a \boccur 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 forloop? 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 printfas alternative.

如果您不喜欢跨越两行的作业,您可以使用printf作为替代。

IFS="$(printf '\n')"