exec '{}'が見つかりません>


8

Execを使用すると、すべての引数を一度{} +に渡すか、1つずつ渡すことができます。{} \;

ここで、すべてのjpegの名前を変更したいとしましょう。問題はありません。

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;

しかし、出力をリダイレクトする必要がある場合は、リダイレクト'{}'後にアクセスできません。

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;

これは機能しません。forループを使用して、findの出力を使用する前に変数に格納する必要があります。認めましょう、面倒です。

for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;

より良い方法はありますか?


>3番目のコードサンプルに欠落はありませんか?
チョロバ2018年

1
あなたの最初の行でさえ非標準であり、したがって移植性はありません。{}このような文字列は通常は展開されないため、長い文字列に現れるコマンドラインは避けてください。
schily 2018年

その最初の例はあなたが言うことをしません。
ctrl-alt-delor 2018年

1
あなたはあなたの質問を修正しました:私はもはやそれを変更しません。
ctrl-alt-delor 2018年

1
価値があるのですが、リダイレクトが期待どおりに機能しない主な理由は、リダイレクトがfind一度起動したシェルによって処理され、findコマンド自体に適用されるためです。{}その文脈では特別な意味を持ちません。リダイレクションはへの議論ではありませんfind、そしてそれは確かに-exec条項の一部ではありません。
ジョンボリンジャー2018年

回答:


13

コマンドbash -cfind -execで使用し、bashコマンドで位置パラメータを使用できます。

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec bash -c 'cjpeg -quality 80 "$1" > "$(dirname "$1")/optimized_$(basename "$1")"' sh {} \;

その方法{}はで提供され$1ます。

sh前で{}内部シェルに「名前」を通知します。ここで使用される文字列は、エラーメッセージなどで使用されます。これについては、stackoverflowに関するこの回答で詳しく説明しています


8

あなたは答えがあります(https://unix.stackexchange.com/a/481687/4778)が、ここに理由があります。

リダイレクト>、パイプ|、および$展開はすべて、コマンドが実行される前にシェルによって実行されます。したがってoptimized_{}findが開始される前に、stdoutはにリダイレクトされます。


3

現在のシェルがリダイレクトを解釈しないように、リダイレクトを引用する必要があります。
しかし、それを引用すると、リダイレクトされるコマンドの出力も回避されます。
これに対する既知の解決策は、シェルを呼び出すことです:

find . -name '*.jpg' -exec sh -c 'echo "$1" >"$1".new' called_shell '{}' \;

この場合、リダイレクト(>)は現在のシェルで引用され、呼び出されたシェル内で正しく機能します。called_shell使用されている$0子シェルのパラメータ(名前)( sh)。

これは、サフィックスにファイル名が追加された場合はうまく機能しますが、プレフィックスを使用した場合はうまく機能しません。接頭辞が機能するためには./、ファイル名の先頭に追加される検索を削除すること${1#./}と、-execdirオプションを使用することの両方が必要です。

あなたは(またはいない場合があります)を使用したい-inameという名前のファイルようにオプションを*.JPG*.JpG、または他のバリエーションも含まれています。

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     cjpeg -quality 80 "$1" > optimized_"${1#./}"
     ' called_shell '{}' \;

また、最後にループ(for f do … ; done)とa +を追加することで、ファイルごとではなく、ディレクトリごとに1回シェルを呼び出すこともできます(またはできません)。

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     for f; do cjpeg -quality 80 "$f" > optimized_"${f#./}"; done
     ' called_shell '{}' \+

そして、最後cjpegに、ファイルに直接書き込むことができるので、次のようにリダイレクトを回避できます。

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     for f; do cjpeg -quality 80 "$f" -outfile optimized_"${f#./}"; done
     ' called_shell '{}' \+

3

cjpeg標準出力ではなく、名前付きファイルに書き込むことができるオプションがあります。のバージョンがオプションをfindサポートしている-execdir場合は、それを利用してリダイレクトを不要にすることができます。

find . \( -name '*.jpg' -o -name '*.jpeg' \) \
  -execdir cjpeg -quality 80 -outfile optimized_'{}' '{}' \;

注:これは、実際のBSD版を前提とfind先行ストリップに見える、./にexandingたときに、ファイル名からを{}。(または逆に、GNU find ./名前に追加します。どの動作が「正しい」かを示す基準はありません。)


findサポートしている場合は-execdir、の代わりに使用できます-exec。このコマンドは、ファイルが見つかったディレクトリで実行し、そして原因{}となりますaa.jpg代わりに./t2/aa.jpg
chepner 2018年

まだエラーが発生しています:cjpeg: can't open optimized_./aa.jpg
Isaac

うーん、それはGNU findとBSDの違いのようfindです。(非標準の拡張機能を使用することの危険。)
chepner 2018年

先頭にファイル名がないと、./エラーが発生しやすくなります。この回答では、合理的な解決策が提案されています
Isaac

1

スクリプトcjq80を作成します。

#!/bin/bash
cjpeg -quality 80 "$1" > "${1%/*}"/optimized_"${1##*/}"

実行可能にする

chmod u+x cjq80

そして、それを-execで使用してください:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjq80 '{}' \;

私はこれについて考えましたが、それはあまり便利ではありません。特に、これをビルドプロセスに組み込む必要があったため、
Buzutは

スクリプトを他のビルドスクリプトに追加するだけで、二重にネストされたbash -cよりも読みやすくなります。
choroba

まあ、それは好みの問題です。今、すべてのオプションが指定されているので、誰かが同じ問題に遭遇した場合、彼は自分のために選択します:)
Buzut
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.