Quale è più efficiente per trovare quali file in un intero filesystem contengono una stringa: grep ricorsivo o trova con grep in unistruzione exec? Presumo che find sarebbe più efficiente perché puoi almeno filtrare se conosci lestensione del file o unespressione regolare che corrisponde al nome del file, ma quando conosci solo -type f
che è meglio ? GNU grep 2.6.3; find (GNU findutils) 4.4.2
Esempio:
grep -r -i "the brown dog" /
find / -type f -exec grep -i "the brown dog" {} \;
Commenti
Risposta
Non sono sicuro:
grep -r -i "the brown dog" /*
è davvero ciò che intendevi. Ciò significherebbe grep ricorsivamente in tutti i file e le directory non nascosti in /
(ma guarda comunque dentro i file nascosti e le directory al loro interno).
Supponendo che intendessi:
grep -r -i "the brown dog" /
Alcune cose da notare:
- Non tutte le
grep
implementazioni supportano-r
. E tra quelli che lo fanno, i comportamenti differiscono: alcuni seguono collegamenti simbolici alle directory quando attraversano lalbero delle directory (il che significa che potresti finire per cercare se veral volte nello stesso file o anche eseguito in cicli infiniti), alcuni no. Alcuni cercheranno nei file del dispositivo (e ci vorrà un po di tempo in/dev/zero
per esempio) o nei pipe o nei file binari …, altri no. - È efficiente in quanto
grep
inizia a cercare allinterno dei file non appena li scopre. Ma mentre cerca in un file, non cerca più altri file in cui cercare (che probabilmente funziona altrettanto bene nella maggior parte dei casi)
Il tuo:
find / -type f -exec grep -i "the brown dog" {} \;
(rimosso il -r
che” non aveva senso qui) è terribilmente inefficiente perché “ne stai eseguendo uno grep
per file. ;
deve essere utilizzato solo per i comandi che accettano un solo argomento. Inoltre qui, poiché grep
guarda solo in un file, non stamperà il nome del file, quindi non saprai dove sono le corrispondenze.
Tu ” non stai guardando dentro i file del dispositivo, pipe, link simbolici …, non stai seguendo link simbolici, ma stai ancora cercando cose come /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
sarebbe molto meglio perché verrebbero eseguiti il minor numero possibile di comandi grep
. Dovresti ottenere il nome del file a meno che lultima 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" {} +
Tieni presente che grep
non verrà avviato fino a find
ha trovato abbastanza file da masticare, quindi ci sarà un certo ritardo iniziale. Inoltre, find
non continuerà a cercare altri file fino a quando non verrà restituito il precedente grep
. Lallocazione e il passaggio dellelenco di file di grandi dimensioni ha un impatto (probabilmente trascurabile), quindi tutto sommato probabilmente sarà meno efficiente di un grep -r
che non segue il collegamento simbolico o guarda allinterno dei dispositivi.
Con strumenti GNU:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Come sopra, pochi grep
verranno eseguite le istanze possibili, ma find
continuerà a cercare altri file mentre la prima chiamata di grep
sta cercando allinterno del primo batch. Questo può essere un vantaggio o meno.Ad esempio, con i dati memorizzati su dischi rigidi rotanti, find
e grep
laccesso ai dati archiviati in posizioni diverse del disco rallenterà il disco velocità effettiva provocando il movimento costante della testina del disco. In una configurazione RAID (dove find
e grep
possono accedere a dischi diversi) o su SSD, ciò potrebbe fare una differenza positiva.
In una configurazione RAID, anche lesecuzione di più invocazioni simultanee grep
potrebbe 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 le prestazioni in modo significativo. Tieni presente, tuttavia, che il secondo grep
verrà avviato solo una volta trovato un numero sufficiente di file per riempire il primo comando grep
. Puoi aggiungere unopzione -n
a xargs
affinché ciò avvenga prima (e passare meno file per grep
invocation).
Tieni inoltre presente che se “reindirizzi xargs
output a qualsiasi cosa tranne che a un dispositivo terminale, il greps
inizieranno a memorizzare nel buffer il loro output, il che significa che loutput di questi grep
sarà probabilmente intercalato in modo errato. “Dovresti utilizzare stdbuf -oL
(dove disponibile come su GNU o FreeBSD) su di loro per aggirare questo problema (potresti ancora avere problemi con righe molto lunghe (tipicamente> 4KiB)) o fare in modo che ciascuno scriva il proprio output in un file separato e concatenali tutti alla fine.
Qui, la stringa che stai cercando è fissa (non una regexp) quindi usare lopzione -F
potrebbe fare la differenza (improbabile come grep
le implementazioni sanno già come ottimizzarlo).
Unaltra cosa che può ld fare una grande differenza è correggere la localizzazione su C se ti trovi in una localizzazione multibyte:
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 vuoi cercare:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Oppure elimina i percorsi che desideri escludere esplicitamente:
LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \ -type f -exec grep -i "the brown dog" /dev/null {} +
Commenti
- Non ‘ suppongo che qualcuno possa indicarmi una risorsa o spiegare cosa significano {} e +. Non cè ‘ nulla che io possa vedere nelle pagine di manuale di exec, grep o che trovi nella scatola di Solaris che ‘ sto usando. È solo la shell che concatena i nomi dei file e li passa a grep?
- @Poldie, che ‘ è chiaramente spiegato nella descrizione del
-exec
predicato nella pagina man di Solaris - Ah, sì. Non ‘ sfuggivo al mio {char durante la ricerca nella pagina man. Il tuo collegamento è migliore; Trovo le pagine di manuale terribili da leggere.
- RAID1 con 3 dischi? Che strano …
- @tink, sì RAID1 è su 2 o più dischi. Con 3 dischi rispetto a 2 dischi, aumenti la ridondanza e le prestazioni di lettura mentre le prestazioni di scrittura sono più o meno le stesse. Con 3 dischi invece di 2, ciò significa che puoi anche correggere gli errori, poiché quando un po gira su una delle copie, ‘ puoi dire quale è giusto controllando tutto 3 copie con 2 dischi, puoi ‘ non dirlo.
Risposta
Se la *
nella chiamata grep
non è importante per te, la prima dovrebbe essere più efficiente come una sola listanza di grep
viene avviata e i fork non sono “liberi. Nella maggior parte dei casi sarà più veloce anche con *
ma nei casi limite lordinamento potrebbe invertirlo.
Potrebbero esserci altre find
– grep
strutture che funzionano meglio soprattutto con molte piccole file. Leggere grandi quantità di voci di file e inode contemporaneamente può dare un miglioramento delle prestazioni sulla rotazione dei media.
Ma diamo uno sguardo alle statistiche di syscall:
trova
> strace -cf find . -type f -exec grep -i -r "the brown dog" {} \; % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 97.86 0.883000 3619 244 wait4 0.53 0.004809 1 9318 4658 open 0.46 0.004165 1 6875 mmap 0.28 0.002555 3 977 732 execve 0.19 0.001677 2 980 735 stat 0.15 0.001366 1 1966 mprotect 0.09 0.000837 0 1820 read 0.09 0.000784 0 5647 close 0.07 0.000604 0 5215 fstat 0.06 0.000537 1 493 munmap 0.05 0.000465 2 244 clone 0.04 0.000356 1 245 245 access 0.03 0.000287 2 134 newfstatat 0.03 0.000235 1 312 openat 0.02 0.000193 0 743 brk 0.01 0.000082 0 245 arch_prctl 0.01 0.000050 0 134 getdents 0.00 0.000045 0 245 futex 0.00 0.000041 0 491 rt_sigaction 0.00 0.000041 0 246 getrlimit 0.00 0.000040 0 489 244 ioctl 0.00 0.000038 0 591 fcntl 0.00 0.000028 0 204 188 lseek 0.00 0.000024 0 489 set_robust_list 0.00 0.000013 0 245 rt_sigprocmask 0.00 0.000012 0 245 set_tid_address 0.00 0.000000 0 1 uname 0.00 0.000000 0 245 fchdir 0.00 0.000000 0 2 1 statfs ------ ----------- ----------- --------- --------- ---------------- 100.00 0.902284 39085 6803 total
solo grep
> strace -cf grep -r -i "the brown dog" . % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 40.00 0.000304 2 134 getdents 31.71 0.000241 0 533 read 18.82 0.000143 0 319 6 openat 4.08 0.000031 4 8 mprotect 3.29 0.000025 0 199 193 lseek 2.11 0.000016 0 401 close 0.00 0.000000 0 38 19 open 0.00 0.000000 0 6 3 stat 0.00 0.000000 0 333 fstat 0.00 0.000000 0 32 mmap 0.00 0.000000 0 4 munmap 0.00 0.000000 0 6 brk 0.00 0.000000 0 2 rt_sigaction 0.00 0.000000 0 1 rt_sigprocmask 0.00 0.000000 0 245 244 ioctl 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 471 fcntl 0.00 0.000000 0 1 getrlimit 0.00 0.000000 0 1 arch_prctl 0.00 0.000000 0 1 futex 0.00 0.000000 0 1 set_tid_address 0.00 0.000000 0 132 newfstatat 0.00 0.000000 0 1 set_robust_list ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000760 2871 466 total
Commenti
- Nella scala della ricerca in un intero filesystem, i fork sono trascurabili. LI / O è ciò che vuoi ridurre.
- Sebbene sia un errore dellOP, il confronto non è corretto, dovresti rimuovere il
-r
flag digrep
quando si utilizzafind
. Puoi vedere che ha cercato più e più volte gli stessi file confrontando il numero diopen
che è successo. - @qwertzguy, no,
-r
dovrebbe essere innocuo poiché-type f
garantisce che nessuno degli argomenti sia directory. È più probabile che iopen()
siano associati agli altri file aperti dagrep
a ogni chiamata (librerie, dati di localizzazione …) ( grazie per la modifica alla mia risposta btw)
Risposta
Se sei su un SSD e cerchi tempo è trascurabile, potresti usare GNU parallel:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Questo eseguirà fino a 8 processi grep contemporaneamente in base a cosa find
trovato.
Questo danneggerà un disco rigido, ma un SSD dovrebbe farcela abbastanza bene.
Risposta
Unaltra cosa da considerare su questa è la seguente.
Le directory che grep dovrà attraversare ricorsivamente conterranno più file rispetto allimpostazione nofile del tuo sistema? (ad es. numero di handle di file aperti, il valore predefinito è 1024 sulla maggior parte delle distribuzioni Linux)
In tal caso, find è sicuramente la strada da percorrere poiché alcune versioni di grep esploderà con un errore Elenco argomenti troppo lungo quando colpisce una directory con più file rispetto al numero massimo di file aperti gestisce limpostazione.
Solo il mio 2 ¢.
Commenti
- Perché
grep
bombardare? Almeno con GNU grep se fornisci un percorso con/
e utilizzi-R
it ‘ Continuerò semplicemente a scorrere le directory. La shell ‘ non espanderà nulla a meno che tu non fornisca shell-glob. Quindi nellesempio fornito (/*
) solo il contenuto della materia/
, non delle sottocartelle che verranno semplicemente enumerate dagrep
, non passato come argomento dalla shell. - Bene, considerando che lOP chiedeva di cercare in modo ricorsivo (ad es. ” grep -r -i ‘ il cane marrone ‘ / * “), ho visto GNU ‘ s grep (almeno versione 2.9) bombardata con: ” -bash: / bin / grep: elenco di argomenti troppo lungo ” utilizzando la ricerca esatta utilizzata dallOP su una directory che conteneva oltre 140.000 sottodirectory.
-exec {} +
form eseguirà meno fork, quindi dovrebbe essere più veloce di-exec {} \;
. Potrebbe essere necessario aggiungere-H
(o-h
) allegrep
opzioni per ottenere esattamente output equivalente.-r
sugrep
per il secondo