$ 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与这样的文件名一起工作吗?
$ 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与这样的文件名一起工作吗?
当前回答
我知道我没有直接回答xargs的问题,但值得一提的是find的-exec选项。
给定以下文件系统:
[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush
0 directories, 4 files
find命令可以处理Dream Theater和King’s x中的空间。因此,使用grep查找每个乐队的鼓手:
[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
在-exec选项中,{}代表包含路径的文件名。请注意,您不必转义它或将它放在引号中。
-exec的终止符(+和\;)之间的区别是+将尽可能多的文件名分组到一个命令行上。而\;将对每个文件名执行命令。
因此,find bands/ -type f -exec grep Drums{} +将导致:
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
and find bands/ type f -exec grep Drums {} \;会导致:
grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"
在grep的情况下,这会产生打印文件名或不打印文件名的副作用。
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
当然,grep的选项-h和-h将控制是否打印文件名,而不管如何调用grep。
xargs
Xargs还可以控制man文件在命令行上的格式。
Xargs默认将所有参数分组到一行。为了做同样的事情,-exec \;使用xargs -l。注意,-t选项告诉xargs在执行命令之前打印该命令。
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater
Drums:Mike Mangini
grep Drums ./bands/Rush
Drums: Neil Peart
grep Drums ./bands/King's X
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth
Drums:Dirk Verbeuren
注意-l选项告诉xargs对每个文件名执行grep。
相对于默认值(即没有-l选项):
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
Xargs可以更好地控制命令行上的文件数量。给-l选项每个命令的最大文件数量。
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]#
请注意,由于-l2, grep执行时有两个文件名。
其他回答
Ls | grep mp3 | sed -n“7p”| xargs -i mplayer {}
注意,在上面的命令中,xargs将为每个文件重新调用mplayer。这对于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结尾的“行”,而不是换行符…我不确定是否有。
就目前的具体情况而言,第一种方法是最简单的;但是,它可能不能泛化到您尚未列出的其他场景。
替代解决方案可能会有所帮助……
您还可以使用Perl在行尾添加一个空字符,然后在xargs中使用-0选项。不像xargs -d '\n'(在已批准的答案中)-这适用于任何地方,包括OS X。
例如,要递归地列出(执行,移动等)可能包含空格或其他有趣字符的MPEG3文件-我会使用:
find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0 ls
(注意:对于过滤,我更喜欢更容易记住的“| grep”语法而不是“find’s”——name参数。)
在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