$ 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使用这样的文件名吗?
Answers:
该xargs
命令将空格字符(制表符,空格,换行符)用作分隔符。您可以使用以下-d
选项将其范围缩小到仅换行符('\ n'):
ls *.mp3 | xargs -d '\n' mplayer
它仅适用于GNU xargs。对于BSD系统,请使用以下-0
选项:
ls *.mp3 | xargs -0 mplayer
此方法更简单,并且也可以与GNU xargs一起使用。
xargs实用程序从标准输入读取空格,制表符,换行符和文件末尾定界的字符串,并以字符串作为参数执行实用程序。
您要避免使用空格作为分隔符。这可以通过更改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
;并非这些程序的所有版本都支持这些选项(尽管有必要这样做)。
find
与-print0
和xargs
与-0
。但是,AFAIK,HP-UX,AIX和Solaris却没有(但我有待纠正:HP-UX 11i没有; Solaris 10没有; AIX 5.x没有;但是它们不是当前版本)。sed
例如,使用以结尾的'lines' '\0'
代替,这并不难'\n'
,而POSIX 2008 getdelim()
将使其易于管理。
Dick.Guertin的答案[1]暗示可以逃避文件名中的空格是此处建议的其他解决方案的一种有价值的替代方法(例如,使用空字符作为分隔符而不是空格)。但这可能更简单-您实际上不需要一个独特的角色。sed可以直接添加转义的空格:
ls | grep ' ' | sed 's| |\\ |g' | xargs ...
此外,仅当您只需要名称中带有空格的文件时,才需要grep 。更一般地讲(例如,当处理一批文件时,其中一些文件有空格,有些文件没有空格),只需跳过grep即可:
ls | sed 's| |\\ |g' | xargs ...
然后,当然,文件名可能具有除空格以外的其他空格(例如,选项卡):
ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...
假设您的sed支持-r(扩展的regex),例如GNU sed或bsd sed的最新版本(例如,FreeBSD,它最初在FreeBSD 8之前拼写了选项-E,并且同时支持-r和-E至少通过FreeBSD 11)。否则,您可以使用基本的正则表达式字符类括号表达式,并在[]
分隔符中手动输入空格和制表符。
[1]这可能更适合作为对该答案的评论或修改,但是目前我没有足够的声誉来评论,只能提出修改建议。由于上面的后者形式(没有grep)会改变Dick.Guertin最初的回答,因此直接编辑可能还是不合适的。
ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}
请注意,在上面的命令中,xargs
将为mplayer
每个文件重新调用。这可能是不希望有的mplayer
,但对于其他目标可能是好的。
mplayer
每个文件都被重新调用。如果您尝试进行操作很重要,例如... | xargs -I{} mplayer -shuffle {}
:尽管,这将以完全确定的顺序进行-shuffle
。
它取决于(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
循环不工作,如果文件名包含换行符; 其他名称即使在名称中使用换行符也可以正常工作(更不用说空格)。出于我的考虑,如果您的文件名包含换行符,则应重命名不带换行符的文件。在文件名周围使用双引号是使循环正常工作的关键。
如果您具有GNU find
和GNU xargs
(或FreeBSD(* BSD?),或Mac OS X),则也可以使用-print0
和-0
选项,如下所示:
find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer
无论名称的内容如何,此方法均有效(文件名中不能出现的两个唯一字符是斜杠和NUL,斜杠在文件路径中不会引起任何问题,因此使用NUL作为名称分隔符可以覆盖所有内容)。但是,如果需要过滤掉前6个条目,则需要一个程序来处理以NUL结尾的“行”而不是换行符...而且我不确定是否有任何行。
对于目前的特定情况,第一种是最简单的。但是,它可能无法一概而论地涵盖您尚未列出的其他情况。
我知道我没有xargs
直接回答问题,但是值得一提find
的是-exec
。
给定以下文件系统:
[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush
0 directories, 4 files
可以执行find命令来处理Dream Theatre和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]#
看到grep
由于导致执行了两个文件名-l2
。
给定这篇文章的具体标题,这是我的建议:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'
想法是将空格转换为任何唯一字符,例如'<',然后将其更改为'\',即反斜杠后跟空格。然后,您可以将其通过管道传递到您喜欢的任何命令中,例如:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo
这里的关键在于'tr'和'sed'命令;并且可以使用除“ <”之外的任何字符,例如“?” 甚至是制表符。
tr
?为什么不只是ls *.mp3 | sed -n '7!b;s/\([[:space:]]\)/\\\1/g;p'
呢?
替代解决方案可能会有所帮助...
您还可以使用Perl -0
在行末添加一个空字符,然后在xargs中使用该选项。与xargs -d'\ n'(在已批准的答案中)不同,它可以在任何地方使用,包括OSX。
例如,要递归列出(执行,移动等)可能包含空格或其他有趣字符的MPEG3文件-我将使用:
find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0 ls
(注意:对于过滤,与“ find的” --name参数相比,我更喜欢使用易于记忆的“ | grep”语法。)
ls |grep mp3 |sed -n "7p"
您可以只使用echo "Lemon Tree.mp3"
。