$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: 'Lemon'  
Playing Tree.mp3.  
File not found: 'Tree.mp3'  

Exiting... (End of file)  

我的命令失败了,因为“Lemon Tree.mp3”文件包含空格,所以xargs认为它是两个文件。我能让find + xargs与这样的文件名一起工作吗?


当前回答

迪克。Guertin的回答[1]表明,可以转义文件名中的空格,这是这里建议的其他解决方案(例如使用空字符作为分隔符而不是空白字符)的有价值的替代方案。但它可以更简单——你真的不需要一个唯一的字符。你可以直接用sed添加转义的空格:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

此外,只有当您只想要名称中有空格的文件时,才需要使用grep。更一般地(例如,当处理一批文件时,其中一些有空格,一些没有),跳过grep:

ls | sed 's| |\\ |g' | xargs ...

然后,当然,文件名可以有其他空格而不是空格(例如,制表符):

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

这假设你有一个支持-r(扩展的正则表达式)的sed,比如GNU sed或最新版本的bsd sed(例如,FreeBSD最初在FreeBSD 8之前拼写选项“-E”,并且至少为了兼容FreeBSD 11而同时支持-r和-E)。否则,您可以使用基本的正则表达式字符类括号表达式,并在[]分隔符中手动输入空格和制表符。

这可能更适合作为一个评论或编辑的答案,但目前我没有足够的声誉来评论,只能建议编辑。因为后面的形式(没有grep)改变了Dick的行为。格汀最初的回答,直接的编辑可能是不合适的。

其他回答

在macOS (Monterey/12向前,我不确定10.15/Catalina过去了多久)上,如果你的文件名或子目录中有空格,你可以使用以下命令:

mdfind  -0 -onlyin . -name .txt | xargs -0 grep stackoverflow |  wc -l

正如珍的回答所示:

xargs实用程序从标准输入中读取空格、制表符、换行符和文件结束符分隔的字符串,并以这些字符串作为参数执行实用程序。

您希望避免使用空格作为分隔符。这可以通过更改xargs的分隔符来实现。根据手册:

-0修改xargs以NUL('' \0 ")字符作为分隔符, 而不是空格和换行。这预计将用于 与find(1)中的-print0函数一致。

macOS 10.12版本。x (Sierra),如果你在文件名或子目录中有空格,你可以使用以下方法:

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l

迪克。Guertin的回答[1]表明,可以转义文件名中的空格,这是这里建议的其他解决方案(例如使用空字符作为分隔符而不是空白字符)的有价值的替代方案。但它可以更简单——你真的不需要一个唯一的字符。你可以直接用sed添加转义的空格:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

此外,只有当您只想要名称中有空格的文件时,才需要使用grep。更一般地(例如,当处理一批文件时,其中一些有空格,一些没有),跳过grep:

ls | sed 's| |\\ |g' | xargs ...

然后,当然,文件名可以有其他空格而不是空格(例如,制表符):

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

这假设你有一个支持-r(扩展的正则表达式)的sed,比如GNU sed或最新版本的bsd sed(例如,FreeBSD最初在FreeBSD 8之前拼写选项“-E”,并且至少为了兼容FreeBSD 11而同时支持-r和-E)。否则,您可以使用基本的正则表达式字符类括号表达式,并在[]分隔符中手动输入空格和制表符。

这可能更适合作为一个评论或编辑的答案,但目前我没有足够的声誉来评论,只能建议编辑。因为后面的形式(没有grep)改变了Dick的行为。格汀最初的回答,直接的编辑可能是不合适的。

MacOS上的xargs没有-d选项,所以这个解决方案使用-0代替。

获取ls每行输出一个文件,然后将换行符转换为空值,并告诉xargs使用空值作为分隔符:

Ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer

这取决于(a)与柠檬(Lemons)相比,你对数字7的依恋程度,以及(b)你的文件名是否包含换行符(以及如果它们包含换行符,你是否愿意重命名它们)。

有很多方法来处理它,但其中一些是:

mplayer Lemon*.mp3

find . -name 'Lemon*.mp3' -exec mplayer {} ';'

i=0
for mp3 in *.mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

for mp3 in *.mp3
do
    case "$mp3" in
    (Lemon*) mplayer "$mp3";;
    esac
done

i=0
find . -name *.mp3 |
while read mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

如果文件名包含换行符,则读循环不起作用;其他的即使在名称中使用换行符也能正常工作(更不用说空格了)。对我来说,如果你的文件名包含换行符,你应该重命名没有换行符的文件。在文件名周围使用双引号是循环正确工作的关键。

如果你有GNU find和GNU xargs(或FreeBSD (*BSD?),或Mac OS X),你也可以使用-print0和-0选项,如:

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer

这与名称的内容无关(文件名中唯一不能出现的两个字符是斜杠和NUL,斜杠在文件路径中不会引起任何问题,因此使用NUL作为名称分隔符可以覆盖所有内容)。然而,如果你需要过滤掉前6个条目,你需要一个程序来处理以NUL结尾的“行”,而不是换行符…我不确定是否有。

就目前的具体情况而言,第一种方法是最简单的;但是,它可能不能泛化到您尚未列出的其他场景。