Utilizzo del punto e virgola (;) vs plus (+) con exec in find


159

Perché c'è una differenza nell'output tra l'utilizzo

find . -exec ls '{}' \+

e

find . -exec ls '{}' \;

Ho ottenuto:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1

Risposte:


249

Questo potrebbe essere meglio illustrato con un esempio. Diciamo che findalza questi file:

file1
file2
file3

Usando -execcon un punto e virgola ( find . -exec ls '{}' \;), verrà eseguito

ls file1
ls file2
ls file3

Ma se usi invece un segno più ( find . -exec ls '{}' \+), quanti più nomi di file possibili vengono passati come argomenti a un singolo comando:

ls file1 file2 file3

Il numero di nomi di file è limitato solo dalla lunghezza massima della riga di comando del sistema. Se il comando supera questa lunghezza, il comando verrà chiamato più volte.


1
Grazie. questo è molto utile per voler ordinare i file risultanti: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas

1
Scemo q: noto che +associato -execè sempre sfuggito, ma +associato -mtimenon lo è. Conosci il motivo? Immagino che l'abitudine di fuggire sia ;associata -exec.
kevinarpe,

3
@kevinarpe, in effetti, lo farei con il gesso ;. Non riesco a immaginare che sia mai necessario scappare+
Martin,

36

Tutte le risposte finora sono corrette. Offro questo come una dimostrazione (per me) più chiara del comportamento descritto usando echoanziché ls:

Con un punto e virgola, il comando echoviene chiamato una volta per file (o altro oggetto filesystem) trovato:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

Con un plus, il comando echoviene chiamato una sola volta. Ogni file trovato viene passato come argomento.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Se findviene visualizzato un numero elevato di risultati, è possibile che il comando chiamato sia strozzato sul numero di argomenti.


1
Non dovresti trovare aggiungere i risultati solo a un numero che rende sicuro passarlo alla shell? Almeno è quello che xargsfanno ... in linea di principio non soffoca mai per troppi argomenti.
Rmano,

1
@Rmano: ho visto find(e xargs) su Solaris emettere più argomenti di quanti ne potessero essere consumati. Il xargs(e find) nelle note di GNU sembra comportarsi in modo più sensato , ma non tutti usano GNU.
Johnsyweb,

2
@Johnsyweb, tutti i POSIX findtenterebbero di evitare di raggiungere il limite del numero di argomenti. E questo include Solaris (almeno 10). Dove può fallire è se fai qualcosa del genere find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +o find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, ma findnon puoi davvero biasimarlo. Nota che GNU è findstata una delle ultime findimplementazioni da supportare +(in passato era una seccatura portare lo script su sistemi GNU).
Stephane Chazelas,

19

Da man find:

comando -exec;

Esegui comando; vero se viene restituito lo stato 0. Tutti i seguenti argomenti da trovare sono considerati argomenti per il comando fino a quando un argomento costituito da ';' è stato incontrato. La stringa '{}' è sostituita dal nome del file corrente che viene elaborato ovunque si presenti negli argomenti al comando, non solo negli argomenti in cui è solo, come in alcune versioni di find. È possibile che entrambe queste costruzioni debbano essere salvate (con un '\') o quotate per proteggerle dall'espansione della shell. Vedere la sezione ESEMPI sec per esempi sull'uso 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 nell'uso dell'opzione -exec;

-exec comando {} +

Questa variante dell'opzione -exec esegue il comando specificato sui file selezionati, ma la riga di comando viene creata aggiungendo alla fine ciascun nome di file selezionato ; il numero totale di invocazioni del comando sarà molto inferiore al numero di file corrispondenti. La riga di comando è costruita nello stesso modo in cui xargs costruisce le sue righe di comando. Nel comando è consentita solo un'istanza di '{}'. Il comando viene eseguito nella directory iniziale.

Quindi, per come lo capisco, \;esegue un comando separato per ogni file trovato da find, mentre \+accoda i file ed esegue un singolo comando su tutti. Il \è un carattere di escape, quindi è:

ls testdir1; ls testdir2 

vs

ls testdir1 testdir2

Fare quanto sopra nella mia shell ha rispecchiato l'output nella tua domanda.

esempio di quando vorresti usare \+

Supponi due file 1.tmpe 2.tmp:

1.tmp:

1
2
3

2.tmp:

0
2
3

Con \;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Considerando che se si utilizza \+(per concatenare i risultati di find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

Quindi in questo caso è la differenza tra diff 1.tmp; diff 2.tmpediff 1.tmp 2.tmp

Ci sono casi in cui \;è appropriato e \+sarà necessario. L'utilizzo \+con rmè uno di questi casi, in cui se si rimuove un numero elevato di file le prestazioni (velocità) saranno superiori \;.


Riesco a leggere anche la pagina man. E l'ho fatto, ma non credo di capire la differenza tra l'utilizzo; vs +
Ankur Agarwal

non penso che il -1 sia giusto, ho spiegato la mia comprensione dell'uomo. Non ho solo copiato l'uomo e me ne sono andato. ma ho modificato la mia risposta per includere un esempio migliore.
matchew,

10

findha una sintassi speciale. Le usi così {}come sono perché hanno un significato da trovare come percorso del file trovato e (la maggior parte) le shell non le interpretano diversamente. È necessaria la barra rovesciata \;perché il punto e virgola ha un significato per la shell, che la consuma prima che findpossa ottenerla. Quindi quello che findvuole vedere DOPO che la shell è fatta, nella lista degli argomenti passati al programma C, è

"-exec", "rm", "{}", ";"

ma è necessario \;sulla riga di comando per ottenere un punto e virgola attraverso la shell agli argomenti.

Puoi cavartela \{\}perché l'interpretazione di shell citata \{\}è giusta {}. Allo stesso modo, puoi usare '{}'.

Quello che non puoi fare è usare

 -exec 'rm {} ;'

perché la shell lo interpreta come un argomento,

"-exec", "rm {};"

e rm {} ;non è il nome di un comando. (Almeno a meno che qualcuno non stia davvero scherzando.)

Aggiornare

la differenza è tra

$ ls file1
$ ls file2

e

$ ls file1 file2

Il +è catenating i nomi su una riga di comando.


1
Capisco quello che stai dicendo. Sto chiedendo qual è la differenza tra l'utilizzo; vs +
Ankur Agarwal

1
scusa, ma hai letto attentamente la mia domanda o il mio commento? Potrebbe essere necessario riformularlo. Perché c'è una diversa o / p quando uso il punto e virgola con exec in find rispetto a quando uso plus con exec in find?
Ankur Agarwal,

2
Questa è un'ottima spiegazione per PERCHÉ il comando è così, che la risposta accettata non copre. Grazie!
Sherwin Yu,

1

La differenza tra ;(punto e virgola) o +(segno più) è il modo in cui gli argomenti vengono passati nel parametro find -exec/ -execdir. Per esempio:

  • usando ;eseguirà più comandi (separatamente per ogni argomento),

    Esempio:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Tutti i seguenti argomenti da findconsiderare come argomenti per il comando.

    La stringa {}viene sostituita dal nome del file corrente in fase di elaborazione.

  • usando +eseguirà i comandi meno possibili (poiché gli argomenti sono combinati insieme). È molto simile a come xargsfunziona il comando, quindi utilizzerà il maggior numero possibile di argomenti per comando per evitare di superare il limite massimo di argomenti per riga.

    Esempio:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    La riga di comando viene creata aggiungendo alla fine ciascun nome di file selezionato.

    È {}consentita solo un'istanza di all'interno del comando.

Guarda anche:


-5

stavamo cercando di trovare un file per le pulizie.

trova . -exec echo {} \; il comando ha funzionato per tutta la notte alla fine nessun risultato.

trova . -exec echo {} \ + ha ottenuto risultati e ha impiegato solo poche ore.

Spero che questo ti aiuti.


2
Questa risposta non spiega come funzionano questi due modi e in che modo differiscono i risultati da essi prodotti.
misko321,
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.