当使用命令的输出时,例如locate
以“人类可读形式”生成路径列表(即\
前面没有空格)的命令的输出,如何将它们的输出重定向到另一个命令?
的输出$ locate [something]
会生成其中包含空格的路径,这将禁止其他程序使用包含空格的路径。例如,如果我要
$ du -h `locate *.doc`
这将对包含空格的所有文件和目录产生错误。 (将刻度线包裹在空格中不起作用)
答案1
您使用的具体原因是什么locate
?这似乎符合您的要求:
find . -type f -name '*doc' -exec du -h "{}" \;
也就是说,如果你真的locate
如果确实想要使用像or 这样的工具find
并将其输入作为参数传递给另一个程序,您可以利用NUL
某些工具提供的分隔输出和输入。 locate
并且find
两者都有一个选项(locate
's-0
和find
's -print0
),这将允许您获得更加编程友好的输出,其xargs
设计用于读取它是 -0
争论:
find . -type f -name '*doc' -print0 | xargs -0 du -h
locate -0 '*doc' | xargs -0 du -h
答案2
如果您想要/需要将同一行传递给多个命令,则可以使用以下方法:
#!/usr/bin/env bash
T="${IFS}" # Save off the Internal Field Separator
IFS=$'\n' # Set it to newline.
while read file_line
do
echo "--"
echo "${file_line}"
echo "--"
done <<< $(locate $1)
IFS="${T} # Set it back to the original IFS.
包裹起来是个好主意任何“”中可能有空格的变量。
答案3
不加引号的命令替换($(...)
或`...`
古老的形式)会在类似 Bourne 的 shell 中调用 split+glob(仅在 zsh 中拆分)。
默认情况下,分割是针对$IFS
包含空格、制表符和换行符(以及 zsh 中的 NUL)的特殊参数的字符完成的,这解释了为什么您的命令不适用于包含空格字符的文件名。
你可以这样做:
(IFS='
' # split on newline only
set -o noglob # disable glob
du -hc -- $(locate '*.doc'))
但如果文件名包含换行符,这仍然会失败。的输出locate
根本无法进行后处理。
大多数locate
实现都有一个-0
选项来输出文件路径NUL
- 分隔。 NUL 是唯一不能出现在文件路径中的字节值,这意味着输出是可后处理的。您只需要按 NUL 进行拆分即可。
在zsh
:
IFS=$'\0'
du -hc -- $(locate -0 '*.doc')
或者,更好的是,使用显式 split-on-NUL 运算符:
du -hc -- ${(0)"$(locate -0 '*.doc')"}
在bash
4.4+ 中,可能是:
readarray -td '' files < <(locate -0 '*.doc')
du -hc -- "${files[@]}"
这仍然留下两个问题:
- 如果
locate
找不到任何文件,du -hc --
将不带参数运行,这意味着它将为您提供当前工作目录的磁盘使用情况。 - 另一方面,如果
locate
找到许多文件,您可能最终会达到系统调用的限制execve()
并得到一个arg 列表太长错误。
通过执行以下操作可以避免这两个问题:
locate -0 '*.doc' | xargs -r0 du -hc --
(-r
并且-0
是来自 GNU 实现的非标准扩展,xargs
就像-h
是来自 GNU 实现的非标准扩展du
)。
然而,这引入了一个新问题:xargs
将运行du
多次来解决execve()
限制,但这意味着您将得到几total
行,并且多次硬链接的文件的磁盘使用情况可能最终会被计算多次。
感谢-r
,如果找不到任何文件,则du
不会运行,但这也意味着您不会得到一行。locate
0 total
使用最新版本的 GNU du
,可以通过以下方式解决这些问题:
locate -0 '*.doc' | du --files0-from=- -hc
这次,文件列表是通过管道而不是参数传递的,因此参数的大小没有限制。这也意味着不需要分配大量内存来存储该列表,并且du
一旦locate
开始输出它就可以开始处理它。