web-dev-qa-db-ja.com

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

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

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

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

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

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

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

11
phunehehe

次のように、findの-print0または-printfオプションをxargsと組み合わせて使用​​してみてください。

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

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

-print0

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

17
Steven D
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

Steven's solution が最善だと思いますが、もう1つの方法は、xargsの-Iフラグを使用することです。これにより、コマンドで引数だけでなく、コマンドの最後に引数を追加します)。これを使用して引数を引用できます。

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

通常は直接-exec ${tgt_process} \{\} +を使用するのが最適ですが、needしてファイルまたはストリーム内のファイル名の確実に区切られたリストをfindから取得するには、何らかの理由で、これを行うことができます:

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

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

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

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

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

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

1
mikeserv

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

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

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

0