我正在尝试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')
吗?因为这增加了\b
IFS(我检查过).可以\b
在文件名中出现吗?我不希望我的代码错误地拆分文件名.
您对for
循环后如何正确处理IFS恢复有任何建议吗?我应该将它存储在临时变量中,还是在子shell中运行IFS和for语句?(以及如何做到这一点?)谢谢
更新 - 将我的伪评论更改为真实答案.
我认为这个答案应该解释你所看到的行为.具体而言,命令替换运算符$()
和反引号将从命令输出中删除尾随换行符.但是,第二个示例中的直接赋值不执行任何命令替换,因此按预期工作.
所以我不敢说我认为你提到的赞成评论不正确.
我认为恢复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)
由于命令替换会删除换行符,正如@DigitalTrauma正确编写的那样,您应该使用不同的POSIX兼容方式.
IFS='
'
Run Code Online (Sandbox Code Playgroud)
写一个换行符并将其分配给它.简单有效.
如果您不喜欢跨越两行的作业,您可以使用printf
替代.
IFS="$(printf '\n')"
Run Code Online (Sandbox Code Playgroud)