Mi ritrovo a cercare costantemente la sintassi di

find . -name "FILENAME" -exec rm {} \; 

principalmente perché non vedo come funziona esattamente la parte -exec. Qual è il significato delle parentesi graffe, della barra rovesciata e del punto e virgola? Esistono altri casi duso per che sintassi?

Commenti

  • @Philippos: Capisco il tuo punto. Tieni presente che le pagine man sono un riferimento, cioè utili per quelli con una comprensione della materia per cercare la sintassi. Per qualcuno che non conosce largomento, spesso sono criptici e formali per essere utili. Scoprirai che la risposta accettata è circa 10 volte più lunga della voce della pagina man, e che ' è per un motivo.
  • Anche la vecchia pagina POSIX man legge Un nome_utilità o un argomento contenente solo i due caratteri " {} " deve essere sostituito d dal percorso corrente , che mi sembra sufficiente. Inoltre ha un esempio con -exec rm {} \;, proprio come nella tua domanda. Ai miei tempi, cerano a malapena altre risorse oltre al " grande muro grigio ", libri di man pagine (la carta era più economica dello spazio di archiviazione). Quindi so che questo è sufficiente per qualcuno che non conosce largomento. La tua ultima domanda però è giusta da porre qui. Sfortunatamente né @Kusalananda né io abbiamo una risposta a questo.
  • @Philippos 🙂 Oh, quella sarebbe una cosa eccitante da provare, per un livello di " eccitante " che è fuori scala del mio grafico.
  • Comeon @Philippos. Stai davvero dicendo a Kusalananda che non ha migliorato la pagina di manuale? 🙂
  • @ZsoltSzilagy Non lho detto né lo intendevo. Ti ha nutrito molto bene, penso solo che tu sia abbastanza grande da mangiare da solo. (-;

Risposta

Questa risposta è suddivisa nelle seguenti parti:

  • Utilizzo di base di -exec
  • Utilizzo di -exec in combinazione con sh -c
  • Utilizzo di -exec ... {} +
  • Utilizzo di -execdir

Utilizzo di base di -exec

Lopzione -exec accetta unutilità esterna con argomenti opzionali come il suo argomento e lo esegue.

Se la stringa {} è presente ovunque nel comando dato, ogni sua istanza verrà sostituita dal percorso attualmente in fase di elaborazione ( ad es. ./some/path/FILENAME). Nella maggior parte delle shell, i due caratteri {} non devono essere citati.

Il comando deve essere terminato con un ; affinché find sappia dove finisce (poiché potrebbero esserci ulteriori opzioni in seguito S). Per proteggere ; dalla shell, deve essere citato come \; o ";" , altrimenti la shell lo vedrà come la fine del comando find.

Esempio (il \ al la fine delle prime due righe è solo per le continuazioni di riga):

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";" 

Questo troverà tutti i file regolari (-type f) i cui nomi corrispondono al pattern *.txt nella o sotto la directory corrente. Quindi verificherà se la stringa hello si verifica in uno dei file trovati utilizzando grep -q (che non produce alcun output, solo unuscita stato). Per quei file che contengono la stringa, verrà eseguito cat per inviare il contenuto del file al terminale.

Ogni -exec agisce anche come un “test” sui nomi di percorso trovati da find, proprio come -type e -name sì. Se il comando restituisce uno stato di uscita zero (che significa “successo”), viene considerata la parte successiva del comando find, altrimenti find il comando continua con il percorso successivo. Viene utilizzato nellesempio sopra per trovare file che contengono la stringa hello, ma per ignorare tutti gli altri file.

Lesempio sopra illustra i due usi più comuni casi di -exec:

  1. Come test per limitare ulteriormente la ricerca.
  2. Per eseguire un qualche tipo di azione sul trovato nome percorso (di solito, ma non necessariamente, alla fine del comando find).

Utilizzo di -exec in combinazione con sh -c

Il comando che -exec può eseguire è limitato a unutilità esterna con argomenti opzionali.Non è possibile utilizzare incorporati della shell, funzioni, condizionali, pipeline, reindirizzamenti ecc. Direttamente con -exec, a meno che non sia racchiuso in qualcosa come sh -c shell secondaria.

Se le funzionalità bash sono richieste, utilizza bash -c al posto di sh -c.

sh -c esegue /bin/sh con uno script fornito dalla riga di comando, seguito da argomenti della riga di comando opzionali per lo script.

Un semplice esempio di utilizzo di sh -c da solo, senza find :

sh -c "echo "You gave me $1, thanks!"" sh "apples" 

Questo passa due argomenti allo script della shell figlio. Questi verranno inseriti in $0 e $1 affinché lo script possa essere utilizzato.

  1. Il stringa sh. Questo sarà disponibile come $0 allinterno dello script e se la shell interna restituisce un messaggio di errore, gli anteporrà questa stringa.

  2. Largomento apples è disponibile come $1 nello script e se ci fossero stati più argomenti, questi sarebbero stati disponibili come $2, $3 ecc. Sarebbero anche disponibili nellelenco "$@" (ad eccezione di $0 che non farebbe parte di "$@").

Questo è utile in combinazione con -exec in quanto ci consente di creare script arbitrariamente complessi che agiscono sui nomi di percorso trovati da find.

Esempio: trova tutti i file regolari che hanno un determinato suffisso del nome file e cambia il suffisso del nome file con un altro suffisso, dove i suffissi sono conservati nelle variabili:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c "mv "$3" "${3%.$1}.$2"" sh "$from" "$to" {} ";" 

Dentro linte rnal script, $1 sarebbe la stringa text, $2 sarebbe la stringa txt e $3 sarebbe il percorso che find ha trovato per noi. Lespansione del parametro ${3%.$1} prenderebbe il percorso e rimuoverebbe il suffisso .text da esso.

Oppure, utilizzando dirname / basename:

find . -type f -name "*.$from" -exec sh -c " mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"" sh "$from" "$to" {} ";" 

oppure, con variabili aggiunte nel script interno:

find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2; pathname=$3 mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"" sh "$from" "$to" {} ";" 

Tieni presente che in questultima variante, le variabili from e to nella shell figlio sono distinte dalle variabili con lo stesso nome nello script esterno.

Quanto sopra è il modo corretto di chiamare uno script complesso arbitrario da -exec con find. Lutilizzo di find in un ciclo come

for pathname in $( find ... ); do 

è soggetto a errori e inelegante (opinione personale). Divide i nomi dei file su spazi bianchi, invoca il globbing dei nomi dei file e forza anche la shell ad espandere il risultato completo di find prima ancora di eseguire la prima iterazione del ciclo.

Vedi anche:


Utilizzo di -exec ... {} +

; alla fine può essere sostituito da +. Questo fa sì che find esegua il comando dato con il maggior numero possibile di argomenti (nomi di percorso trovati) anziché una volta per ogni percorso trovato. La stringa {} deve comparire appena prima di + affinché funzioni .

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} + 

Qui, find raccoglierà i nomi di percorso risultanti ed eseguirà cat su più di essi possibile contemporaneamente.

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec mv -t /tmp/files_with_hello/ {} + 

Allo stesso modo qui, mv verrà eseguito come poche volte possibile. Questultimo esempio richiede GNU mv di coreutils (che supporta lopzione -t).

Usare -exec sh -c ... {} + è anche un modo efficiente per eseguire il ciclo su un insieme di nomi di percorso con uno script arbitrariamente complesso.

Le basi sono le stesse di quando si utilizza -exec sh -c ... {} ";", ma lo script ora richiede un elenco di argomenti molto più lungo. Questi possono essere ripetuti eseguendo un ciclo su "$@" allinterno dello script.

Il nostro esempio dallultima sezione che cambia i suffissi del nome del file:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2 shift 2 # remove the first two arguments from the list # because in this case these are *not* pathnames # given to us by find for pathname do # or: for pathname in "$@"; do mv "$pathname" "${pathname%.$from}.$to" done" sh "$from" "$to" {} + 

Utilizzo di -execdir

Cè anche -execdir ( implementato dalla maggior parte delle find varianti, ma non unopzione standard).

Funziona come -exec con la differenza che il comando di shell specificato viene eseguito con la directory del percorso trovato come directory di lavoro corrente e che {} conterrà il nome base del percorso trovato senza il suo percorso (ma GNU find farà ancora anteporre al nome base ./, mentre BSD find non lo farà).

Esempio:

find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \; 

Questo sposterà ogni *.txt -file trovato in una done-texts sottodirectory preesistente nella stessa directory in cui si trovava il file trovato . Il file verrà anche rinominato aggiungendo il suffisso .done.

Sarebbe un po più complicato da fare con -exec poiché dovremmo ottenere il nome di base del file trovato da {} per formare il nuovo nome del file. Abbiamo anche bisogno del nome della directory da {} per individuare correttamente la directory done-texts.

Con -execdir, alcune cose come queste diventano più semplici.

Loperazione corrispondente utilizzando -exec invece di -execdir dovrebbe utilizzare una shell figlia:

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done" done" sh {} + 

o

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "${name%/*}/done-texts/${name##*/}.done" done" sh {} + 

Commenti

  • -exec prende un programma e argomenti e lo esegue; alcuni comandi della shell consistono solo di un programma e di argomenti, ma molti no. Un comando di shell può includere reindirizzamento e piping; -exec non può (sebbene lintero find possa essere reindirizzato). Un comando di shell può utilizzare ; && if ecc; -exec non può, anche se -a -o può fare alcune cose. Un comando di shell può essere un alias, una funzione di shell o incorporato; -exec non può. Un comando di shell può espandere vars; -exec non può (anche se la shell esterna che esegue find può farlo). Un comando della shell può sostituire $(command) in modo diverso ogni volta; -exec non può. …
  • Dicendolo ' un comando della shell è sbagliato qui, find -exec cmd arg \; non ' t invoca una shell per interpretare una riga di comando della shell, esegue execlp("cmd", "arg") direttamente, non execlp("sh", "-c", "cmd arg") (per cui il shell finirebbe per fare lequivalente di execlp("cmd", "arg") se cmd non fosse integrato).
  • Potresti chiarire che tutto gli find argomenti dopo -exec e fino a ; o + crea il comando da eseguire insieme ai suoi argomenti, con ogni istanza di un argomento {} sostituita con il file corrente (con ;) e {} come ultimo argomento prima di + sostituito con un elenco di file come argomenti separati (nel {} + case). IOW -exec accetta diversi argomenti, terminati da un ; o {} +.
  • @Kusalananda ' Il tuo ultimo esempio funziona anche con questo comando più semplice: find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'?
  • @Atralb Sì, anche questo avrebbe funzionato e avrebbe avuto lo stesso effetto dellultimo pezzo di codice, ma invece di eseguire mv in un ciclo, una volta per file trovato, esegui sia sh e mv per ogni file trovato, che sarà notevolmente più lento per grandi quantità di file.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *