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

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

私はBashに初めて触れていて、表面的に非常に簡単に見えることをやろうとしています。ディレクトリ階層でfindを実行してすべての* .wmaファイルをインポートし、出力をmp3に変換するコマンドにパイプし、変換されたファイルを.mp3として保存します。私の考えでは、コマンドは次のようになるはずです(オーディオ変換コマンドを放棄し、代わりに説明のためにエコーを使用しました)。

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

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

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

それで、私の質問は(「私がここで何を間違っているのですか?」という具体的な質問に加えて)、文字列操作でパイプ操作の結果である値をパイプ操作の結果である値とは異なる方法で処理する必要がありますか?変数の割り当て?

ベストアンサー1

他の答えがすでに設定されているため、${f%.*}コマンドを実行する前にシェルによって拡張されますxargs。各ファイル名を一度拡張し、シェル変数をファイル名に設定する必要がありますf(パスでは-I fそうしません。シェル変数の概念がなく、コマンドで文字列を検索します。xargsたとえば、f使用するとxargs -I e echo …何かをします./somedir/somefile.wmacho .mp3)。コマンド。

このアプローチを継続してxargs呼び出しシェルに拡張を実行できることを通知します。より良い点は、Tellはfind正確にxargs使用するのが難しいかなり古いツールであるということです。なぜなら、最新バージョンの find は同じ作業(およびそれ以上)を実行しますが、パイプラインの難易度が少ない構造を持つからです。代わりfind … -print0 | xargs -0 command …にを実行してくださいfind … -exec command … {} +

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

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

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

findかなり新しいシェル(ksh93、bash ≥4.0、またはzsh)を実行すると仮定すると、ここでは実際に使用する必要はありません。 bashでサブディレクトリ(kshでは)を繰り返すには、globモードを有効にするには入力shopt -s globstarします。だからあなたは実行することができます.bashrc**/set -o globstar

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

(という名前のディレクトリがある場合は、*.wmaループ[ -f "$f" ] || continueの先頭に追加してください。)

おすすめ記事