trova exec '{}' non disponibile dopo>


8

Exec ci consente di passare tutti gli argomenti contemporaneamente {} +o di passarli uno alla volta con{} \;

Ora diciamo che voglio rinominare tutto jpeg , nessun problema nel farlo:

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

Ma se devo reindirizzare l'output, '{}'non è accessibile dopo il reindirizzamento.

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

Questo non funzionerebbe. Dovrei usare un ciclo for, archiviando l'output di find in una variabile prima di usarlo. Ammettiamolo, è ingombrante.

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

Quindi c'è un modo migliore?


Non manca un esempio >nel terzo codice?
Choroba,

1
Anche la tua prima linea è non standard e quindi non portatile. Cerca di evitare le righe di comando in cui {}appare una stringa più lunga in quanto tali stringhe non vengono generalmente espanse.
schily

Quel primo esempio non fa quello che dici.
ctrl-alt-delor

1
Hai risolto la tua domanda: non la cambierei più.
ctrl-alt-delor

1
Per quello che vale, il motivo principale per cui il reindirizzamento non funziona come desiderato è che viene gestito dalla shell da cui viene avviato find, una volta, e applicato al findcomando stesso. Non {}ha un significato speciale in quel contesto. Il reindirizzamento non è un argomento finde certamente non fa parte della -execclausola.
John Bollinger,

Risposte:


13

È possibile utilizzare bash -call'interno del find -execcomando e utilizzare il parametro posizionale con il comando bash:

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

In questo modo {}viene fornito $1.

Il shprima che {}dice alla shell interna il suo "nome", la stringa qui usata viene usata ad es. Nei messaggi di errore. Questo è discusso più in questa risposta su StackOverflow .



3

Il reindirizzamento deve essere citato per evitare che l'attuale shell lo interpreti.
Ma citandolo eviterà anche il reindirizzamento dell'output del comando.
La soluzione nota a questo è quella di chiamare una shell:

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

In questo caso, il reindirizzamento ( >) viene quotato nella shell corrente e funziona correttamente all'interno della shell chiamata. Il called_shellviene utilizzato come $0parametro (il nome) del guscio bambino ( sh).

Funziona bene se viene aggiunto un suffisso al nome del file, ma non se si utilizza un prefisso. Affinché un prefisso funzioni è necessario sia rimuovere ./ciò che trova anteporre ai nomi di file ${1#./}sia utilizzare l' -execdiropzione.

Si può (o non può) desidera utilizzare l' -inameopzione in modo che i file di nome *.JPGo di *.JpGo di altre variazioni sono inclusi anche.

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

E potresti anche (o non voler) anche chiamare la shell una volta per directory anziché una volta per file aggiungendo un loop ( for f do … ; done) e un +alla fine:

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

E, infine, poiché cjpegè in grado di scrivere direttamente su un file, il reindirizzamento potrebbe essere evitato come:

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

3

cjpegha un'opzione che ti permette di scrivere su un file denominato, piuttosto che sullo standard output. Se la tua versione findsupporta l' -execdiropzione, puoi sfruttarla per rendere inutile il reindirizzamento.

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

Nota: questo presuppone in realtà la versione BSD di find, che sembra ./rimuovere il lead dal nome del file quando si espande a {}. (O al contrario, GNU find aggiunge ./ al nome. Non esiste uno standard per dire quale comportamento sia "giusto".)


Se il tuo findsupporto -execdir, puoi usarlo invece di -exec. Fa sì che il comando venga eseguito nella directory in cui è stato trovato il file e {}diventerà aa.jpginvece di ./t2/aa.jpg.
Chepner,

Ancora in errore: cjpeg: can't open optimized_./aa.jpg.
Isaac,

Hm, questa sembra essere una differenza tra GNU finde BSD find. (I pericoli dell'utilizzo di estensioni non standard.)
Chepner

Un nome di file senza un carattere iniziale ./sembra essere più soggetto a errori. Una soluzione ragionevole è proposta in questa risposta .
Isaac,

1

Crea uno script cjq80:

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

Renderlo eseguibile

chmod u+x cjq80

E usalo in -exec:

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

Ci avevo pensato, ma non è molto utile. Soprattutto perché ho dovuto incorporarlo in un processo di compilazione
Buzut,

Basta aggiungere lo script ad altri script di build, lo trovo più leggibile rispetto a bash -c doppiamente nidificato.
Choroba,

Bene, è una questione di gusti. Ora ogni opzione è specificata, quindi se qualcuno incontra lo stesso problema, sceglierà per se stesso :)
Buzut
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.