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

  • Matematica / informatica / efficienza degli algoritmi in ‘ t opinione basato.
  • Seleziona questo. Sebbene non ricorsivo, darebbe una comprensione su quale è meglio. unix.stackexchange.com/questions/47983/…
  • @AvinashRaj he ‘ non sta chiedendo opinione. Egli ‘ ‘ sta chiedendo quale sia più efficiente e / o più veloce , non quale è ” migliore “. Questa è una domanda perfettamente rispondente che ha ununica risposta specifica che dipende da come questi due programmi svolgono il loro lavoro e da cosa esattamente gli dai da cercare.
  • Tieni presente che -exec {} + form eseguirà meno fork, quindi dovrebbe essere più veloce di -exec {} \;. Potrebbe essere necessario aggiungere -H (o -h) alle grep opzioni per ottenere esattamente output equivalente.
  • Probabilmente non ‘ volevi lopzione -r su grep per il secondo

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 findgrep 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 di grep quando si utilizza find. Puoi vedere che ha cercato più e più volte gli stessi file confrontando il numero di open che è successo.
  • @qwertzguy, no, -r dovrebbe essere innocuo poiché -type f garantisce che nessuno degli argomenti sia directory. È più probabile che i open() siano associati agli altri file aperti da grep 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 da grep, 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.

Lascia un commento

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