web-dev-qa-db-ja.com

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

私は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
...

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

10
Howard Dierking

他の回答ではすでに特定されているため、xargsコマンドを実行する前に、シェルによって${f%.*}が展開されます。シェル変数fをファイル名に設定して、ファイル名ごとにこの拡張を行う必要があります(-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"' {} \;

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

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

*.wmaというディレクトリがある場合は、ループの最初に[ -f "$f" ] || continueを追加します。)

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

xargs-I fを使用した解決策:

% 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,'
6
0xFF