如何将`find`的结果作为文件列表传递?


11

情况是,我有一个MP3播放器mpg321,它接受文件列表作为参数。我将音乐保存在一个名为“ music”的目录中,在该目录中还有更多目录。我只想播放所有这些,所以我用

mpg321 $(find /music -iname "*\.mp3")

。问题是,某些文件名中包含空格,程序将这些文件分成较小的部分,并抱怨缺少文件。包装find引号的结果

mpg321 "$(find /music -iname "*\.mp3")"

这无济于事,因为所有人都会变成一个很大的“文件名”,显然找不到。

那我该怎么做呢?如果那很重要,我正在使用bash,但zsh很快就会切换到。

Answers:


17

尝试结合使用find -print0-printfoption,xargs如下所示:

find /music -iname "*\.mp3" -print0 | xargs -0 mpg321

find的手册页解释了它是如何工作

-打印0

真正; 在标准输出上打印完整的文件名,后跟一个空字符(而不是-print使用的换行符)。这允许包含换行符或其他类型的空格的文件名可以由处理查找输出的程序正确解释。此选项对应于xargs的-0选项。


对不起,所有的编辑。我似乎还记得-print0 xarg -0模式在其他类型的空白上失败,但是我找不到示例。
史蒂文D

哦,是的!但我仍然想知道为什么mpg321 $(find /music -iname "*\.mp3" -print0)不呢?
phunehehe

1
好吧,我认为它不起作用有两个原因:(1)文件名由空字符分隔,这根本不是mpg321所期望的,并且(2)xargs正在做转义另一个空白的工作,而不是找。
史蒂文D

2
@ phunehehe,@ Steven:mpg321与它无关,它是将输入输出find分解成单独参数的shell 。并且-print0 | xargs -0可以使用所有可能的文件名。
吉尔斯(Gilles)'所以

8
find /music -iname "*\.mp3" -exec mpg123 {} +

使用GNU find,您还可以使用-print0xargs -0,但是学习另一个工具毫无意义。该-exec ... {} +语法变得很少提及因为Linux不迟于收购它-print0,但没有理由现在不使用它。

使用zsh或bash 4,这要简单得多:

mpg123 **/*.[Mm][Pp]3

仅在zsh中,可以使模式(的一部分)不区分大小写:

mpg123 (#i)**/*.mp3

对此+1,“ find -print0”是一个丑陋的hack。
p静态

2

我认为Steven的解决方案是最好的,但是另一种方法是使用xargs的-I标志,它使您可以指定一个字符串,然后在命令中将其替换为参数(而不是仅将参数附加到命令的末尾)。您可以使用它来引用参数:

find /music -iname "*\.mp3" | xargs -0 -Ifoo mpg321 "foo"

我不明白这个答案...您正在使用xargs的-0标志,而没有find的标志-print0。同样,xargs的-I标志也有许多后果。不像普通的xargs将所有行从stdin压缩为一个命令的普通文件,它xargs -I可能执行mpg321数百或数千次(每个文件一次),这肯定不是目的。还请记住,在xargs内部也不需要引号foo 。请注意,如果您将所有文件名压缩到line行并将其发送到xargs -I,它们将不会打开。
2015年

1

通常最好直接访问,-exec ${tgt_process} \{\} +但是如果出于某种原因确实需要从文件或流中获取可靠分隔的文件名列表find,则可以执行以下操作:

find -exec sh -c 'printf "///%s///\n" "$@"' -- \{\} +

您从中得到的是两个唯一的字符串。每个文件名的开头是字符串\n///,每个文件名的末尾是字符串///\nfind不管文件名包含什么字符,这两个字符串都不会在输出中的其他任何位置出现,除了在那些位置。

此外,以上用法是POSIX便携式基准,可以在几乎所有的Unix系统上使用。尽管有人建议使用空字节定界符(尽管很方便),但事实并非如此。

但是,同样,这仅在-exec$tgt_process出于任何原因而无法直接进行时才是必要的,因为那应该是您的目标。一方面,上述方法仍然需要解析。例如,如果您希望用引号将每个文件名shell括起来,则首先必须确保转义了文件名中的所有硬引号:

find ... + | sed 's|'\''|&"&"&|g;s|///|'\''|g'

这将输出一个正确的由外壳程序转义的文件名数组,无论它们的构成字符是什么。现在,您只希望希望接收端的应用程序不会损坏它。


0

另一种方法是转义文件名中的所有特殊字符。例如:

find /music -iname "*\.mp3" | sed 's!\([] \*\$\/&[]\)!\\\1!g' | xargs mpg321

基本上,这会将正确转义的文件名传递给xargs以便执行,不会有任何问题。


1
这仍然在文件名的换行符上中断。您可能也很sed 's|.|\\&|g'高兴-POSIX还是建议这么做。
mikeserv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.