私は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
...
だから私の質問(特定の「私がここで何を間違っているのか」を除いて)はこれです-パイプ操作の結果である値は、変数割り当ての結果である値とは異なる文字列操作操作で処理する必要があります?
他の回答ではすでに特定されているため、xargs
コマンドを実行する前に、シェルによって${f%.*}
が展開されます。シェル変数f
をファイル名に設定して、ファイル名ごとにこの拡張を行う必要があります(-I f
を渡してもそれはできません:xargs
には何の概念もありませんシェル変数。コマンド内で文字列f
を検索するため、たとえばxargs -I e echo …
を使用した場合、./somedir/somefile.wmacho .mp3
などのコマンドが実行されます。
このアプローチを継続して、xargs
に拡張を実行できるシェルを呼び出すように指示します。 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' _ {} +
引数_
は、シェルでは$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,'