Qual è più efficiente per trovare quali file in un intero filesystem contengono una stringa:grep ricorsivo o trova con grep in un'istruzione exec? Presumo che la ricerca sarebbe più efficiente perché puoi almeno filtrare se conosci l'estensione del file o una regex che corrisponde al nome del file, ma quando conosci solo -type f
che è migliore? GNU grep 2.6.3; trova (GNU findutils) 4.4.2
Esempio:
grep -r -i 'the brown dog' /
find / -type f -exec grep -i 'the brown dog' {} ;
Risposta accettata:
Non sono sicuro:
grep -r -i 'the brown dog' /*
è proprio quello che intendevi. Ciò significherebbe grep ricorsivamente in tutti i file e le directory non nascosti in /
(ma guarda ancora all'interno dei file nascosti e delle directory all'interno di quelli).
Supponendo che tu intendessi:
grep -r -i 'the brown dog' /
Alcune cose da notare:
- Non tutti i
grep
le implementazioni supportano-r
. E tra quelli che lo fanno, i comportamenti differiscono:alcuni seguono collegamenti simbolici alle directory quando attraversano l'albero delle directory (il che significa che potresti finire per cercare più volte nello stesso file o addirittura eseguire in loop infiniti), altri no. Alcuni guarderanno all'interno dei file del dispositivo (e ci vorrà del tempo in/dev/zero
per esempio) o pipe o file binari…, alcuni no. - È efficiente come
grep
inizia a cercare all'interno dei file non appena li scopre. Ma mentre cerca in un file, non cerca più altri file in cui cercare (cosa che probabilmente è altrettanto buona nella maggior parte dei casi)
Tuo:
find / -type f -exec grep -i 'the brown dog' {} ;
(rimosso il -r
che non aveva senso qui) è terribilmente inefficiente perché stai eseguendo un grep
per file. ;
dovrebbe essere usato solo per comandi che accettano un solo argomento. Inoltre qui, perché grep
cerca solo in un file, non stamperà il nome del file, quindi non saprai dove sono le corrispondenze.
Non stai guardando all'interno di file del dispositivo, pipe, collegamenti simbolici..., non stai seguendo collegamenti simbolici, ma potenzialmente stai ancora guardando all'interno di cose come /proc/mem
.
find / -type f -exec grep -i 'the brown dog' {} +
sarebbe molto meglio perché come pochi grep
verrebbero eseguiti i comandi possibili. Otterresti il nome del file a meno che l'ultima esecuzione non abbia un solo file. Per questo è meglio usare:
find / -type f -exec grep -i 'the brown dog' /dev/null {} +
o con GNU grep
:
find / -type f -exec grep -Hi 'the brown dog' {} +
Nota che grep
non verrà avviato fino a find
ha trovato abbastanza file da masticare, quindi ci sarà un certo ritardo iniziale. E find
non continuerà a cercare altri file fino al precedente grep
è ritornato. L'allocazione e il passaggio dell'elenco di file di grandi dimensioni ha un impatto (probabilmente trascurabile), quindi tutto sommato probabilmente sarà meno efficiente di un grep -r
che non segue un collegamento simbolico o guarda all'interno dei dispositivi.
Con gli strumenti GNU:
find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'
Come sopra, come pochi grep
verranno eseguite più istanze possibili, ma find
continuerà a cercare più file mentre il primo grep
l'invocazione sta guardando all'interno del primo batch. Questo può o non può essere un vantaggio però. Ad esempio, con i dati archiviati su dischi rigidi rotazionali, find
e grep
l'accesso ai dati archiviati in posizioni diverse sul disco rallenterà la velocità effettiva del disco facendo muovere costantemente la testina del disco. In una configurazione RAID (dove find
e grep
potrebbe accedere a diversi dischi) o su SSD, ciò potrebbe fare una differenza positiva.
In una configurazione RAID, eseguendo diversi simultanei grep
le invocazioni potrebbero anche migliorare le cose. Sempre con strumenti GNU su storage RAID1 con 3 dischi,
find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'
potrebbe aumentare notevolmente le prestazioni. Nota comunque che il secondo grep
verrà avviato solo una volta trovati file sufficienti per riempire il primo grep
comando. Puoi aggiungere un -n
opzione per xargs
affinché ciò avvenga prima (e passare meno file per grep
invocazione).
Tieni inoltre presente che se stai reindirizzando xargs
output su qualsiasi cosa tranne un dispositivo terminale, quindi greps
s inizierà a memorizzare nel buffer il proprio output, il che significa che l'output di quei grep
s sarà probabilmente interfogliato in modo errato. Dovresti usare stdbuf -oL
(ove disponibile come su GNU o FreeBSD) su di loro per aggirare il problema (potresti ancora avere problemi con righe molto lunghe (in genere>4KiB)) o far scrivere a ciascuno il proprio output in un file separato e concatenarli tutti alla fine.
Qui, la stringa che stai cercando è fissa (non un'espressione regolare), quindi usando il -F
l'opzione potrebbe fare la differenza (improbabile come grep
le implementazioni sanno già come ottimizzarlo).
Un'altra cosa che potrebbe fare una grande differenza è correggere le impostazioni internazionali su C se ti trovi in una locale multi-byte:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'
Per evitare di guardare dentro /proc
, /sys
…, usa -xdev
e specifica i file system in cui desideri eseguire la ricerca:
LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +
Oppure elimina i percorsi che desideri escludere in modo esplicito:
LC_ALL=C find / ( -path /dev -o -path /proc -o -path /sys ) -prune -o
-type f -exec grep -i 'the brown dog' /dev/null {} +