findコマンドからパイプされたファイル名を操作する


10

私はBashに比較的慣れていないため、表面上は非常に簡単に思える何かを実行しようとしています。ディレクトリ階層全体でfindを実行して、すべての* .wmaファイルを取得し、その出力をコマンドにパイプして、mp3に変換します。変換したファイルを.mp3として保存します。私の考えは、コマンドは次のようになるはずだと考えていました(オーディオ変換コマンドは省略し、代わりに説明のためにエコーを使用しています)。

$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3

私が理解しているように、-print0引数を使用すると、スペースを含むファイル名を処理できます(これらの多くは、音楽ファイルであるので、これを行います)。次に、(xargsの結果として)findからの各ファイルパスがfにキャプチャされ、文字列の最後から部分文字列の一致/削除を使用すると、mp3で元のファイルパスをエコーする必要があることを期待していますwmaの代わりに拡張子。ただし、この結果の代わりに、次のように表示されます。

*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...

だから私の質問(特定の「ここで何が間違っているのか」を除いて)はこれです-パイプ操作の結果である値は、変数割り当ての結果である値とは異なる文字列操作操作で処理する必要があります?


1
で使用xargsする理由はありませんfind-execオプションで付属しています。使用するコマンドを質問に追加するだけで、誰かが正しいfindコマンドを見せてくれますか?
ixtmixilix 2013年

はい(以下で説明します)、findコマンドの各結果({}メンバーなど)に対して文字列操作を実行できる限り
ハワードディアキング

実際にxargsは、より適切なエッジケースがありますexec。適例については、このスタックポストstackoverflow.com/questions/896808/find-exec-cmd-vs-xargsを参照してください。
d34dh0r53 2013年

@ d34dh0r53どんなエッジケース?リンク先のスレッドはポイントしていません。
Gilles 'SO-悪をやめる'

回答:


8

他の答えはすでに特定${f%.*}されているので、xargsコマンドを実行する前にシェルによって展開されます。シェル変数fをファイル名に設定して、この拡張がファイル名ごとに1回発生する必要があります(渡すこと-I fはそれを行いません。xargsシェル変数の概念がないfため、コマンドで文字列を検索します。使用xargs -I e echo …されます(例:)のようなコマンドが実行されます./somedir/somefile.wmacho .mp3

このアプローチxargsを継続して、拡張を実行できるシェルを呼び出すように指示します。より良い、教えてくださいfindxargs最新バージョンのfindは、同じ作業(およびそれ以上)を実行する構成があり、配管の問題が少ないので、ほとんど時代遅れのツールであり、正しく使用することは困難です。の代わりにfind … -print0 | xargs -0 command …、実行find … -exec command … {} +ます。

find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +

引数_$0シェルにあります。ファイル名は、for f; do …ループする位置引数として渡されます。このコマンドのより単純なバージョンは、ファイルごとに個別のシェルを実行します。これは同等ですが、少し遅くなります。

find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;

findかなり最近のシェル(ksh93、bash≥4.0、またはzsh)を実行している場合は、実際にここを使用する必要はありません。bashでは、サブディレクトリで再帰するglobパターンをアクティブshopt -s globstarにする.bashrcためにを入れます**/(kshでは、それはset -o globstar)。その後、実行できます

for f in **/*.wma; do
  echo "${f%.*}.mp3"
done

(と呼ばれるディレクトリがある場合は、ループの先頭に*.wma追加[ -f "$f" ] || continueします。)


素晴らしい説明だけでなく、気づかなかった方向に向けられた(グロスター機能を取得するためにbashシェルをアップグレードする)-結局のところ、再帰的グロビングは、コマンドをシンプルに保ち、実行しようとしていたすべてのことを達成するソリューションでした。ありがとう!
ハワードディアキング、2013年

6

ソリューションの場合、$ {f%。*}。mp3の評価は、xargsによって分岐されたシェルではなく、コマンド全体を呼び出すシェルで行われます。シェルにはf変数はなく、空の文字列で置き換えられます。

-I fでxargsを使用するソリューション:

% cat 1.sh 
#!/bin/sh
f=$1
echo ${f%.*}.mp3
% /bin/ls -1
1.sh
aaa.wma
bbbb.wma
ccccc.wma
% find ./ -name '*.wma' -type f -print0 | xargs -0 -I f ./1.sh f
./aaa.mp3
./ccccc.mp3
./bbbb.mp3

しかし、私はこのようにします:

% find ./ -name '*.wma' -type f | sed 's,\.wma$,.mp3,'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.