Answers:
理想情况下,您根本不会那样做,因为在shell脚本中正确地解析文件名总是很困难的(将其修复为空格,其他嵌入式字符(尤其是换行符)仍然会遇到问题)。这甚至被列为BashPitfalls页面中的第一项。
也就是说,有一种方法可以几乎完成您想要的操作:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
记住$i
在使用它时也要引用,以避免以后其他事情解释空格。还请记住$IFS
在使用后重新设置,因为不这样做会在以后引起令人困惑的错误。
这确实附加了另一个警告:while
循环内发生的事情可能发生在子shell中,具体取决于您所使用的shell,因此变量设置可能不会持久。在for
这一点,但在价格,即使你的应用循环版本避免$IFS
的解决方案,以避免问题与空间,然后你会惹上麻烦,如果find
返回的文件太多。
在某种程度上,所有这些的正确解决方案变为使用Perl或Python等语言而不是Shell进行。
您可以将“内部字段分隔符”(IFS
)设置为用于循环参数拆分的空格以外的其他值,例如
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
我IFS
认为它在find中使用后会重置,主要是因为它看起来不错。将其设置为换行符,我还没有发现任何问题,但是我认为这是“更干净的”。
根据您要对输出进行的处理find
,另一种方法是直接-exec
与find
命令一起使用,或者使用-print0
并将其通过管道传递到xargs -0
。在第一种情况下find
,请注意文件名的转义。在这种-print0
情况下,find
使用空分隔符打印其输出,然后xargs
在此分隔。由于没有文件名可以包含该字符(我所知道的),因此这始终是安全的。这在简单情况下最有用;通常不是完整for
循环的理想替代品。
find -print0
与xargs -0
采用find -print0
联合xargs -0
反对合法的文件名完全健壮,并且是可扩展性最强的方法之一。例如,假设您要列出当前目录中的每个PDF文件。你可以写
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 echo
这将-iname '*.pdf'
在当前目录(.
)和任何子目录中找到(通过)每个PDF,并将每个PDF 作为参数传递给echo
命令。因为我们指定了该-n 1
选项,xargs
所以一次只能将一个参数传递给echo
。如果我们省略了该选项,xargs
则应尽可能多地传递给echo
。(您可以echo short input | xargs --show-limits
查看命令行中允许多少字节。)
xargs
做什么的?我们可以清楚地看到使用输入脚本xargs
的效果,-n
特别是通过输入的脚本,该脚本以比更为精确的方式回显其参数echo
。
$ cat > echoArgs.sh <<'EOF'
#!/bin/bash
echo "Number of arguments: $#"
[[ $# -eq 0 ]] && exit
for i in $(seq 1 $#); do
echo "Arg $i: <$1>"
shift
done
EOF
$ find . -iname '*.pdf' -print0 | xargs -0 ./echoArgs.sh
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 ./echoArgs.sh
请注意,它可以很好地处理空格和换行符,
$ touch 'A space-age
new line of vending machines.pdf'
$ find . -iname '*space*' -print0 | xargs -0 -n 1 ./echoArgs.sh
以下常见解决方案尤其麻烦:
chmod +x ./echoArgs.sh
for file in $(ls *spacey*); do
./echoArgs.sh "$file"
done
笔记
我不同意bash
basher,因为bash
与* nix工具集一起,它非常擅长处理文件(包括名称中嵌入了空格的文件)。
实际上,find
您可以选择要处理的文件进行精细控制。在bash方面,您实际上只需要意识到必须将字符串放入bash words
;即可。通常使用“双引号”或其他一些机制(例如使用IFS或find的{}
请注意,在大多数情况下,您无需设置和重置IFS;只需在本地使用IFS,如下面的示例所示。所有这三个都可以处理空格。同样,您也不需要“标准”循环结构,因为find的 \;
实际上是一个循环。只需将循环逻辑放入bash函数中(如果您未调用标准工具)。
IFS=$'\n' find ~/ -name '*.txt' -exec function-or-util {} \;
还有两个例子
IFS=$'\n' find ~/ -name '*.txt' -exec printf 'Hello %s\n' {} \;
IFS=$'\n' find ~/ -name '*.txt' -exec echo {} \+ |sed 's/home//'
'找到also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+ instead
\;`)
find -print0
和的原因xargs -0
。