「検索」の結果をファイルのリストとして渡す方法は?


11

状況としては、mpg321ファイルのリストを引数として受け入れるMP3プレーヤーがあります。私は「music」という名前のディレクトリに音楽を保存します。このディレクトリにはさらにいくつかのディレクトリがあります。全部プレイしたいので、

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

。問題は、一部のファイル名に空白が含まれていて、プログラムがそれらの名前を小さな部分に分割し、欠落しているファイルについて文句を言うことです。の結果をfind引用符で囲む

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

すべてが1つの大きな「ファイル名」になりますが、これは明らかに見つかりません。

どうすればこれを行うことができますか?それが重要であれば、私はを使用bashしていますが、zshすぐに切り替えます。

回答:


17

find -print0-printfオプションを次のxargsように組み合わせて使用してみてください:

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

これがどのように機能するかはfindのマニュアルページで説明されています

-print0

真; 標準出力に完全なファイル名を出力し、その後に(-printが使用する改行文字の代わりに)null文字を出力します。これにより、改行またはその他の種類の空白を含むファイル名が、検索出力を処理するプログラムによって正しく解釈されます。このオプションは、xargsの-0オプションに対応しています。


すべての編集でごめんなさい。他の種類の空白で-print0 xarg -0パターンが失敗したことを覚えているようですが、例を見つけることができませんでした。
Steven D

ああそうだ!しかし、なぜ私はまだそうmpg321 $(find /music -iname "*\.mp3" -print0)しないのだろうか?
phunehehe

1
まあ、私はそれが2つの理由で機能しないと考えています:(1)ファイル名がnull文字で区切られている、これはmpg321がまったく期待していないこと、および(2)xargsが他の空白をエスケープする作業をしている見つける。
Steven D

2
@ phunehehe、@ Steven:それとmpg321は何の関係もありませんfind。出力を個別の引数に分割するのはシェルです。そして-print0 | xargs -0、可能なすべてのファイル名で動作します。
Gilles「SO-邪悪なことをやめなさい」

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

GNU findでは、-print0およびxargs -0も使用できますが、さらに別のツールを学ぶ意味はほとんどありません。-exec ... {} +Linuxは後でよりも、それを取得しているため、構文は少し言及を取得します-print0が、今それを使用しない理由はありません。

zshまたはbash 4では、これははるかに簡単です。

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

zshでのみ、(aの一部)パターンで大文字と小文字を区別しないようにできます。

mpg123 (#i)**/*.mp3

これに+1して、「find -print0」は醜いハックです。
p-static

2

Stevenの解決策が最善だと思いますが、もう1つの方法は、xargs -Iフラグを使用することです。これにより、(コマンドの最後に引数を追加するのではなく)コマンドで引数に置き換える文字列を指定できます。これを使用して引数を引用できます。

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

私はこの答えを理解できません... find -0なしでxargsのフラグを使用しています-print0。また、xargsの-Iフラグには多くの影響があります。xargsstdinからのすべての行を1つのコマンドに圧縮する単なるプレーンとは異なり、(ファイルごとに1回)数百回または数千回xargs -I実行されるmpg321可能性がありますが、これは意図されていません。またxargs、内部ではfooを引用する必要がないことにも注意してください。すべてのファイル名をline行に圧縮してに送信するとxargs -I、ファイルが開かないことに注意してください。
シックス・

1

通常は直接実行するの-exec ${tgt_process} \{\} +が最善ですが、何らかの理由でファイルまたはストリーム内のファイル名の確実に区切られたリストを取得する必要がある場合はfind、次のようにできます。

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

それから得られるのは、2つのユニークな文字列です。すべてのファイル名の先頭には文字列が\n///あり、すべてのファイル名の末尾には文字列があります///\n。これらの2つの文字列はfind、ファイル名に含まれる文字に関係なく、これらの位置を除いて、の出力の他の場所では発生しません。

さらに、上記の使用法はPOSIXポータブルのベースラインであり、ほぼすべてのUNIXシステムでの動作に依存できます。これは、その便利さにもかかわらず、他の人が推奨するnullバイト区切り文字の使用には当てはまりません。

しかし、これもまた、何らかの理由で直接-exec自分ができない場合にのみ必要です$tgt_process。1つには、上記の方法でも解析が必要です。たとえば、各ファイル名のシェルを引用する場合は、最初にファイル名のハードクォートがエスケープされていることを確認する必要があります。

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

これにより、構成文字が何であれ、シェルでエスケープされたファイル名の配列が出力されます。これで、受信側のアプリケーションがそれを壊さないことを期待する必要があります。


0

これを行う別の方法は、ファイル名に含まれるすべての特殊文字をエスケープすることです。例えば:

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

これは基本的に、適切にエスケープされたファイル名を実行のためにxargsに渡し、問題は発生しません。


1
これはまだファイル名の改行で壊れます。そして、あなたも同様にsed 's|.|\\&|g'-それはとにかくPOSIXが推奨することです。
mikeserv 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.