$ 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
命令将空白字符(制表符、空格、换行符)作为分隔符。
您可以使用以下选项仅针对换行符 ('\n') 缩小范围-d
:
ls *.mp3 | xargs -d '\n' mplayer
它仅适用于 GNU xargs。
对于 MacOS:
ls *.mp3 | tr \\n \\0 | xargs -0 mplayer
更简单且实用的方法(当不需要进一步处理文件名时):
mplayer *.mp3
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 . -name \*.mp3 -print0 | xargs -0 mplayer
代替
ls | grep mp3
MacOS 上的 xargs 没有 -d 选项,因此此解决方案使用 -0 代替。
获取 ls 以每行输出一个文件,然后将换行符转换为空值并告诉 xargs 使用空值作为分隔符:
ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer
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 ...
假设您有一个支持 -r(扩展正则表达式)的 sed,例如 GNU sed 或最新版本的 bsd sed(例如,FreeBSD 在 FreeBSD 8 之前最初拼写选项“-E”并同时支持 -r 和 -E 以实现兼容性至少通过 FreeBSD 11)。否则,您可以使用基本的正则表达式字符类括号表达式并在分隔符中手动输入空格和制表[]
符。
[1] 这可能更适合作为对该答案的评论或编辑,但目前我没有足够的声誉发表评论,只能建议编辑。由于上面的后一种形式(没有 grep)改变了 Dick.Guertin 原始答案的行为,因此直接编辑可能无论如何都不合适。
find . -name 'Lemon*.mp3' -print0 | xargs -0 -i mplayer '{}'
在我的情况下,这有助于删除带有空格的不同文件。它也应该适用于 mplayer。必要的技巧是引号。(在 Linux Xubuntu 14.04 上测试。)
我知道我没有xargs
直接回答这个问题,但值得一提find
的是 's -exec
option。
给定以下文件系统:
[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
's 选项将控制是否打印文件名,而不管如何-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]#
看到它grep
是用两个文件名执行的,因为-l2
.
ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}
请注意,在上面的命令中,将为每个文件重新xargs
调用。mplayer
这对于 可能是不可取的mplayer
,但对于其他目标可能没问题。
在 macOS 10.12.x (Sierra) 上,如果文件名或子目录中有空格,则可以使用以下内容:
find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l
这取决于(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
则循环不起作用;即使名称中有换行符(更不用说空格),其他人也可以正常工作。为了我的钱,如果您的文件名包含换行符,您应该重命名文件而不使用换行符。在文件名周围使用双引号是循环正常工作的关键。
如果您有 GNUfind
和 GNU xargs
(或 FreeBSD (*BSD?),或 Mac OS X),您还可以使用-print0
and-0
选项,如下所示:
find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer
无论名称的内容如何,这都有效(文件名中唯一不能出现的两个字符是斜杠和 NUL,斜杠不会导致文件路径出现问题,因此使用 NUL 作为名称分隔符涵盖了所有内容)。但是,如果您需要过滤掉前 6 个条目,则需要一个程序来处理以 NUL 而不是换行符结尾的“行”……我不确定是否有。
对于手头的具体情况,第一个是迄今为止最简单的;但是,它可能无法概括涵盖您尚未列出的其他场景。
替代解决方案可能会有所帮助...
您还可以使用 Perl 在行尾添加一个空字符,然后使用-0
xargs 中的选项。与 xargs -d '\n' (在批准的答案中)不同 - 这适用于任何地方,包括 OS X。
例如,要递归列出(执行、移动等)可能包含空格或其他有趣字符的 MPEG3 文件 - 我会使用:
find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0 ls
(注意:对于过滤,我更喜欢更容易记住的“| grep”语法而不是“find's”--name 参数。)
鉴于这篇文章的具体标题,这是我的建议:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'
想法是将空格转换为任何唯一字符,例如“<”,然后将其更改为“\”,即反斜杠后跟空格。然后,您可以将其通过管道传输到您喜欢的任何命令中,例如:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo
这里的关键在于“tr”和“sed”命令;并且您可以使用除“<”之外的任何字符,例如“?” 甚至是制表符。
在 macOS 上(Monterey/12 向前,我不确定超过 10.15/Catalina 有多远),如果文件名或子目录中有空格,则可以使用以下内容:
mdfind -0 -onlyin . -name .txt | xargs -0 grep stackoverflow | wc -l
正如 Jen 的回答所指出的:
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).