shell - temp IFS仅作为换行符.为什么这不起作用:IFS = $(echo -e'\n')

6 bash shell ifs

我正在尝试for在shell中使用空格迭代文件名.我在读计算器的问题IFS=$'\n'可以被使用,这似乎很好地工作.然后,在评论中读到一个答案,使其与除了可以使用的bash之外的shell兼容IFS=$(echo -e '\n').

前者有效,后者无效.该评论被多次上调.我检查了输出,变量似乎不包含换行符.

#!/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
Run Code Online (Sandbox Code Playgroud)


输出显示第一个字段分隔符丢失:

0000000
Found one
two two
three
Run Code Online (Sandbox Code Playgroud)


但是第二个是正确的:

0000000 0a
0000001
Found one
Found two two
Found three
Run Code Online (Sandbox Code Playgroud)


我找到了一个已回答的问题,可能与我的问题重复或不同:
linux - 当设置IFS拆分换行时,为什么需要包含退格?- 堆栈溢出

做那里讨论的事情有危险IFS=$(echo -en '\n\b')吗?因为这增加了\bIFS(我检查过).可以\b在文件名中出现吗?我不希望我的代码错误地拆分文件名.

您对for循环后如何正确处理IFS恢复有任何建议吗?我应该将它存储在临时变量中,还是在子shell中运行IFS和for语句?(以及如何做到这一点?)谢谢

Dig*_*uma 6

更新 - 将我的伪评论更改为真实答案.

我认为这个答案应该解释你所看到的行为.具体而言,命令替换运算符$()和反引号将从命令输出中删除尾随换行符.但是,第二个示例中的直接赋值不执行任何命令替换,因此按预期工作.

所以我不敢说我​​认为你提到的赞成评论不正确.


我认为恢复IFS最安全的方法是将其设置在子shell中.您需要做的就是将相关命令放在括号中():

(
    IFS=$'\n'
    echo -n "$IFS" | od -t x1
    for file in `printf 'one\ntwo two\nthree'`; do
        echo "Found $file"
    done
)
Run Code Online (Sandbox Code Playgroud)

当然,调用子shell会产生一个小延迟,因此如果要重复多次,则需要考虑性能.


另外,要非常小心,文件名可以包含\ b和\n.我认为他们不能包含的唯一字符是斜线和\ 0.至少这就是这篇维基百科文章所说的内容.

$ touch $'12345\b67890'
$ touch "new
> line"
$ ls --show-control-chars
123467890  new
line
$ 
Run Code Online (Sandbox Code Playgroud)


Ric*_*lli 5

由于命令替换会删除换行符,正如@DigitalTrauma正确编写的那样,您应该使用不同的POSIX兼容方式.

IFS='
'
Run Code Online (Sandbox Code Playgroud)

写一个换行符并将其分配给它.简单有效.

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

IFS="$(printf '\n')"
Run Code Online (Sandbox Code Playgroud)