$ 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
命令将空白字符(制表符、空格、换行符)视为定界符。
您可以使用 -d
选项仅针对换行符('\n')进行操作,如下所示:
ls *.mp3 | xargs -d '\n' mplayer
它仅适用于GNU xargs。
对于MacOS:
ls *.mp3 | tr \\n \\0 | xargs -0 mplayer
更加简单实用的方法(当不需要进一步处理文件名时):
mplayer *.mp3
ls
是一个不好的主意。这种情况的正确解决方法是简单地使用 mplayer *.mp3
,而不是尝试使用 xargs
。 - tripleeexargs实用程序从标准输入读取以空格、制表符、换行符和文件结束符为分隔符的字符串,并将这些字符串作为参数来执行某个命令。
如果您想避免使用空格作为分隔符,可以通过更改xargs的分隔符来实现。根据手册:
例如:-0 Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines. This is expected to be used in concert with the -print0 function in find(1).
find . -name "*.mp3" -print0 | xargs -0 mplayer
回答如何播放第七个mp3的问题,更简单的方法是运行原始代码。 mplayer "$(ls *.mp3 | sed -n 7p)"
find
和GNU的xargs
,并非所有版本的这些程序都支持这些选项(尽管有理由认为它们应该支持)。 - Jonathan Leffler-print0
的find
和带有-0
的xargs
。据我所知,HP-UX、AIX和Solaris没有这个功能(但我可能会被纠正:HP-UX 11i没有;Solaris 10没有;AIX 5.x没有;但它们不是当前版本)。例如,将sed
更改为使用以'\0'
结尾的“行”而不是'\n'
不难,并且POSIX 2008 getdelim()
可以轻松管理。 - Jonathan Lefflertr
而不是perl)应该成为一个答案! - Ahmed Fasihfind . -name \*.mp3 -print0 | xargs -0 mplayer
而不是
ls | grep mp3
在MacOS上,xargs没有-d选项,因此此解决方案使用-0代替。
让ls每行输出一个文件名,然后将换行符转换为NULL,并告诉xargs使用NULL作为分隔符:
ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer
Dick.Guertin的答案[1]建议在文件名中转义空格是其他解决方案(如使用null字符作为分隔符而不是空格)的有价值的替代方案。但可能更简单 - 您实际上不需要一个唯一的字符。您可以让sed直接添加转义空格:
ls | grep ' ' | sed 's| |\\ |g' | xargs ...
ls | sed 's| |\\ |g' | xargs ...
ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...
find . -name 'Lemon*.mp3' -print0 | xargs -0 -i mplayer '{}'
这对我有帮助,可以删除带空格的不同文件。对于mplayer也应该适用。必要的技巧是引号。(在Linux Xubuntu 14.04上测试过。)
我知道我没有直接回答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"
执行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
也可以控制命令行上有多少个文件。
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
命令被执行了两个文件名。
在 macOS 10.12.x(Sierra)中,如果文件名或子目录中有空格,您可以使用以下方法:
find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l
ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}
注意,在上面的命令中,xargs
会为每个文件调用一个新的mplayer
。这可能对mplayer
不利,但对其他目标可能没有问题。
mplayer
。如果您尝试例如 ... | xargs -I{} mplayer -shuffle {}
:尽管使用了 -shuffle
,但播放顺序仍然完全确定。 - user743382xargs
通常与接受文件名列表的命令一起使用(例如rm
),并尝试将尽可能多的文件名传递给每个调用,只有在需要时才会分成多个调用。当您使用每次调用都可见的命令时(如默认值的echo
),您可以看到差异:seq 0 100000 | xargs
在第一行上打印从0到23695(平台特定,但这是我的系统发生的事情),第2行打印从45539开始,以此类推。而且您是对的,对于大多数命令来说,这并不重要。 - user743382这取决于(a)您对数字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
read
循环无法处理文件名中包含换行符的情况,而其他循环即使文件名中包含换行符(更不用说空格了)也能正确处理。我建议,如果您有包含换行符的文件名,应将文件重命名为不包含换行符的文件名。在文件名周围使用双引号是使循环正常工作的关键。find
和GNU xargs
(或FreeBSD (* BSD?)或Mac OS X),也可以使用-print0
和-0
选项,例如:find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer