Shell 使xargs处理包含空格的文件名

Shell 使xargs处理包含空格的文件名,shell,find,xargs,Shell,Find,Xargs,我的命令失败,因为文件“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) 而不是 find . -name \*.mp3

我的命令失败,因为文件“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)  
而不是

find . -name \*.mp3 -print0 | xargs -0 mplayer

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).
回答关于播放第七个mp3的问题;运行起来更简单

 find . -name "*.mp3" -print0 | xargs -0 mplayer
这取决于(a)您对数字7的依附程度,而不是柠檬,以及(b)您的任何文件名是否包含换行符(如果包含换行符,您是否愿意重命名它们)

解决这一问题的方法有很多,但其中一些是:

 mplayer "$(ls *.mp3 | sed -n 7p)"
如果文件名包含换行符,
read
循环不起作用;即使名称中有换行符(更不用说空格),其他名称也能正常工作。就我个人而言,如果文件名包含换行符,那么应该重命名不包含换行符的文件。在文件名周围使用双引号是循环正常工作的关键

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

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


第一个是目前为止最简单的具体案例;但是,它可能无法概括涵盖您尚未列出的其他场景。

ls | grep mp3 | sed-n“7p”| xargs-i mplayer{}


注意,在上面的命令中,
xargs
将为每个文件重新调用
mplayer
。对于
mplayer
,这可能是不需要的,但是对于其他目标,这可能是正常的。

使用
xargs
命令将空格字符(制表符、空格、新行)作为分隔符

您只能使用
-d
选项将其缩小为新行字符('\n'),如下所示:

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer
它仅适用于GNUxargs

对于MacOS:

ls *.mp3 | xargs -d '\n' mplayer
更简单、更实用的方法(当不需要进一步处理文件名时):


在我的例子中,这有助于删除带有空格的不同文件。它也应该适用于mplayer。必要的技巧是引用。(在Linux 14.04上进行了测试。)

鉴于本文的具体标题,以下是我的建议:

find . -name 'Lemon*.mp3' -print0 | xargs -­0 -i mplayer '{}' 

ls | grep'| tr'Dick.Guertin的回答[1]表明,可以对文件名中的空格进行转义,这是本文建议的其他解决方案(例如使用空字符作为分隔符而不是空格)的一个有价值的替代方案。但这可能更简单——你并不真的需要一个独特的角色。您可以让sed直接添加转义空格:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo
此外,只有当您只需要名称中带有空格的文件时,才需要grep。更一般地说(例如,当处理一批文件时,有些文件有空格,有些没有空格),只需跳过grep:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...
当然,文件名可能有除空白以外的其他空白(例如,选项卡):

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


[1] 这也许更适合作为对答案的评论或编辑,但目前我没有足够的声誉来评论,只能建议编辑。由于上面的后一种形式(没有grep)改变了Dick.Guertin原始答案的行为,直接编辑可能无论如何都不合适。

我知道我不是直接回答
xargs
问题,但值得一提的是
find
-exec
选项

给定以下文件系统:

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...
可以使用find命令处理梦剧场和国王X中的空间。因此,要使用grep查找每个乐队的鼓手:

[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush

0 directories, 4 files
-exec
选项中,
{}
代表包含路径的文件名。请注意,您不必转义它或将其置于引号中

-exec
的终止符(
+
\;
)之间的区别在于
+
将尽可能多的文件名分组到一个命令行上。鉴于
\
将对每个文件名执行命令

因此,
find bands/-type f-exec 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
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
find bands/-type f-exec 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
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
grep
的情况下,这会产生打印或不打印文件名的副作用

grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"
当然,
grep
的选项
-h
-h
将控制是否打印文件名,而不管如何调用
grep


xargs
xargs
还可以控制man文件在命令行中的显示方式

默认情况下,
xargs
将所有参数分组到一行。为了做与
-exec\相同的事情
使用的是
xargs-l
。请注意,
-t
选项告诉
xargs
在执行命令之前打印命令

[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
请参阅
-l
选项告诉xargs对每个文件名执行grep

相对于默认值(即否
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"
[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
[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
[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
[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]# 
find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0  ls
find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l