Perché find -exec mv {} ./target/ + non funziona?


98

Voglio sapere esattamente cosa {} \;e {} \+e | xargs ...cosa fare. Si prega di chiarirli con spiegazioni.

Di seguito 3 comandi vengono eseguiti e restituiscono lo stesso risultato, ma il primo comando richiede un po 'di tempo e anche il formato è leggermente diverso.

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

È perché il primo esegue il filecomando per ogni file proveniente dal findcomando. Quindi, fondamentalmente funziona come:

file file1.txt
file file2.txt

Ma gli ultimi 2 trovano con i -execcomandi esegui il comando file una volta per tutti i file come di seguito:

file file1.txt file2.txt

Quindi eseguo i seguenti comandi su cui il primo viene eseguito senza problemi ma il secondo dà un messaggio di errore.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

Per il comando con {} \+, mi dà il messaggio di errore

find: missing argument to `-exec'

perché? qualcuno può spiegare cosa sto facendo di sbagliato?


la vera domanda è semplice, perché il primo funziona e il secondo no? (1) trova. -type f -iname ' .cpp' -exec mv {} ./test/ \; (2) trova. -type f -iname ' .cpp' -exec mv {} ./test/ \ +
Shahadat Hossain

Risposte:


185

La pagina di manuale (o il manuale GNU in linea ) spiega praticamente tutto.

trova -exec comando {} \;

Per ogni risultato, command {}viene eseguito. Tutte le occorrenze di {}vengono sostituite dal nome del file. ;è preceduto da una barra per impedire alla shell di interpretarlo.

trova -exec comando {} +

Ogni risultato viene aggiunto commanded eseguito successivamente. Tenendo conto dei limiti di lunghezza del comando, immagino che questo comando possa essere eseguito più volte, con la pagina di manuale che mi supporta:

il numero totale di invocazioni del comando sarà molto inferiore al numero di file corrispondenti.

Nota questa citazione dalla pagina del manuale:

La riga di comando è costruita più o meno nello stesso modo in cui xargs costruisce le sue righe di comando

Ecco perché non sono consentiti caratteri tra {}e ad +eccezione degli spazi bianchi. +fa in modo che find rilevi che gli argomenti dovrebbero essere aggiunti al comando proprio come xargs.

La soluzione

Fortunatamente, l'implementazione GNU di mvpuò accettare la directory di destinazione come argomento, con uno -to il parametro più lungo --target. Il suo utilizzo sarà:

mv -t target file1 file2 ...

Il tuo findcomando diventa:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

Dalla pagina del manuale:

-exec comando;

Eseguire il comando; true se viene restituito lo stato 0. Tutti gli argomenti seguenti per find vengono considerati argomenti del comando fino a quando un argomento composto da `; ' è incontrato. La stringa "{}" è sostituita dal nome del file corrente in fase di elaborazione ovunque si trovi negli argomenti del comando, non solo negli argomenti in cui è solo, come in alcune versioni di find. Potrebbe essere necessario eseguire l'escape (con una "\") o le virgolette per entrambe queste costruzioni per proteggerle dall'espansione della shell. Vedere la sezione ESEMPI per esempi di utilizzo dell'opzione -exec. Il comando specificato viene eseguito una volta per ogni file corrispondente. Il comando viene eseguito nella directory iniziale. Ci sono inevitabili problemi di sicurezza che circondano l'uso dell'azione -exec; dovresti invece usare l'opzione -execdir.

-exec comando {} +

Questa variante dell'azione -exec esegue il comando specificato sui file selezionati, ma la riga di comando viene creata aggiungendo ogni nome di file selezionato alla fine; il numero totale di invocazioni del comando sarà molto inferiore al numero di file corrispondenti. La riga di comando è costruita più o meno nello stesso modo in cui xargs costruisce le sue righe di comando. È consentita una sola istanza di "{}" all'interno del comando. Il comando viene eseguito nella directory iniziale.


1
In realtà so come funziona, ho letto più volte questo manuale, ma ho ricevuto un messaggio di errore per l'utilizzo di {} +, sebbene funzioni per {} \; e sto usando Cygwin in Windows.
Shahadat Hossain

1
@ Shahadat: hai letto la parte prima di "Dalla pagina di manuale"? Hai messo ./test/tra {}e +, ma non sono consentiti caratteri diversi da spazi vuoti tra questi.
Lekensteyn

ru dicendo che non dovrei mettere ./test/ tra {} e +. Allora come funzionerà il comando mv; mv ha bisogno della sorgente che è {} e ha bisogno della destinazione che è ./test/ e la terminazione con +. puoi per favore scrivere il comando cosa pensi giusto?
Shahadat Hossain

@ Shahadat: vedo cosa stai cercando di ottenere. Windows è lento nell'esecuzione dei programmi, quindi è necessario combinarlo con un comando. Aggiungerò un'alternativa alla risposta.
Lekensteyn

1
Il +comando è un po 'strano AFAIU poiché attacca i file alla "fine" (e non al posto di {}) quindi perché usarli {}- questo è fonte di confusione. Grazie per l' -topzione che non conoscevo, sembra che sia stata creata come soluzione alternativa a quel -exec +problema!
e2-e4

6

Ho riscontrato lo stesso problema su Mac OSX , utilizzando una shell ZSH : in questo caso non c'è alcuna -topzione per mv, quindi ho dovuto trovare un'altra soluzione. Tuttavia il seguente comando è riuscito:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

Il segreto era citare le parentesi graffe . Non è necessario che le parentesi graffe siano alla fine del execcomando.

Ho provato con Ubuntu 14.04 (con shell BASH e ZSH ), funziona allo stesso modo.

Tuttavia, quando si utilizza il +segno, sembra davvero che debba essere alla fine del execcomando.


{}deve essere citato nelle shell fishe rc, ma non in zsh, bashné in altre shell delle famiglie Bourne o csh.
Stephane Chazelas

@StephaneChazelas Sì, testato nuovamente sotto Ubuntu con bash, in effetti le virgolette non sono necessarie. Curiosamente, ho avuto un problema se non li citavo in MacOS (utilizzando zsh). Ma non ho un Mac a portata di mano per riprovare ...
arvymetal

3

L'equivalente standard di find -iname ... -exec mv -t dest {} +per findimplementazioni che non supportano -inameo mvimplementazioni che non supportano -tè usare una shell per riordinare gli argomenti:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

Usando -name '*.[cC][pP][pP]', evitiamo anche di fare affidamento sulle impostazioni locali correnti per decidere qual è la versione maiuscola di co p.

Si noti che +, contrariamente a ;non è speciale in nessuna shell, quindi non è necessario che sia quotato (anche se il quoting non danneggerà, tranne ovviamente con shell come rcche non supportano \come operatore quoting).

Il finale /in /dest/dir/è in modo tale che mvnon riesce con un errore, invece di rinominare foo.cppa /dest/dirnel caso in cui uno solo cppè stato trovato il file e /dest/dirnon esiste o non è una directory (o link simbolico alla directory).


+1 ... l'operazione in-shell come preliminare all'esecuzione di un comando è effettivamente utile per una varietà di casi d'uso ... bello.
Cbhihe

0
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+

Per favore aggiungi qualche spiegazione alla tua risposta in modo che altri possano imparare da essa
Nico Haase

Devi rispondere alla domanda, che ha chiesto spiegazioni. Il solo codice non è una risposta.
Lajos Arpad

-1

no, la differenza tra +e \;dovrebbe essere invertita. +aggiunge i file alla fine del comando exec quindi esegue il comando exec ed \;esegue il comando per ogni file.

Il problema è che non find . -type f -iname '*.cpp' -exec mv {} ./test/ \+dovrebbe essere find . -type f -iname '*.cpp' -exec mv {} ./test/ + necessario sfuggire o terminare il file+

xargs non lo uso da molto tempo ma penso che funzioni come +.


Ho provato anche con questo ma ho ricevuto lo stesso messaggio di errore. Inoltre, ovunque ho scoperto di usare solo + ma nel mio cygwin devo usare \ + o "+" per funzionare.
Shahadat Hossain

oh questo è un ambiente cygwin. Scusa, non lo so, non uso la shell cygwin, uso solo un * nix.
Mike Ramirez

1
@Shahadat Hossain provo -name "*.cpp"Non uso quasi mai -iname a meno che non voglia fare qualche difficile ricerca di espressioni regolari, come -iname '??? work. * \. Cpp'
Mike Ramirez

1
@ Mike: penso che tu fraintenda la differenza tra -inamee -name. -inameè la versione -namesenza distinzione tra maiuscole e minuscole e non presenta differenze nella gestione delle espressioni regolari. Suggerisco di provare i comandi prima di postare, il tuo comando fallisce anche nella mia shell.
Lekensteyn

1
@Lekensteyn Era già stabilito che fosse il caso prima del tuo commento. Pensavo di aver riconosciuto Shahadat prima del tuo post, era un semplice "ok". No, non l'ho eseguito manualmente, l'ho fatto dalla parte superiore della mia testa e raramente uso quella forma di ricerca regex con trova. Era solo una cosa tipo "potrebbe aiutare".
Mike Ramirez
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.