Perché il comando "trova | grep 'nome file' "molto più lento di" trova "nome file" "?


10

Ho provato entrambi i comandi e il comando find | grep 'filename' è molte volte più lento del semplice find 'filename' comando.

Quale sarebbe una spiegazione corretta per questo comportamento?


2
Stai elencando ogni file con find e quindi passando i dati a grep per l'elaborazione. Con find usato da solo ti manca il passaggio di passare ogni file elencato a grep per analizzare l'output. Questo sarà quindi più veloce.
Raman Sailopal,

Più lento in che senso? Il completamento dei comandi richiede un tempo diverso?
Kusalananda

1
Non riesco a riprodurlo localmente. Se non altro, time find "$HOME" -name '.profile'segnala un tempo più lungo di time find "$HOME" | grep -F '.profile'. (17s contro 12s).
Kusalananda

2
@JenniferAnderson Ho corso entrambi ripetutamente. I 17 e 12 secondi sono medie. E sì, la grepvariazione corrisponderà ovunque nel findrisultato, mentre la corrispondenza con find -namecorrisponderebbe esattamente (in questo caso).
Kusalananda

2
Sì, find filename sarebbe veloce . Ho pensato che si trattasse di un errore di battitura e che l'OP intendesse find -name filename. Con find filename, solo filenamesarebbe esaminato (e nient'altro).
Kusalananda

Risposte:


11

(Sto assumendo GNU findqui)

Usando solo

find filename

sarebbe veloce, perché sarebbe semplicemente restituito filename, o i nomi all'interno filenamese fosse una directory, o un errore se quel nome non esistesse nella directory corrente. È un'operazione molto rapida, simile a ls filename(ma ricorsiva se filenameè una directory).

In contrasto,

find | grep filename

consentirebbe finddi generare un elenco di tutti i nomi dalla directory corrente e inferiore, che grepquindi filtrerebbe. Questa sarebbe ovviamente un'operazione molto più lenta.

Suppongo che ciò che era effettivamente previsto fosse

find . -type f -name 'filename'

Questo cercherebbe filenamecome nome di un file normale in qualsiasi punto della directory corrente o in basso.

Sarà rapido (o comparativamente rapido) find | grep filename, ma la grepsoluzione corrisponderebbe filenameal percorso completo di ciascun nome trovato, in modo simile a quello che -path '*filename*'farebbe find.


La confusione deriva da un malinteso su come findfunziona.

L'utilità accetta numerosi percorsi e restituisce tutti i nomi al di sotto di questi percorsi.

È quindi possibile limitare i nomi restituiti utilizzando vari test che possono agire sul nome file, sul percorso, sul timestamp, sulla dimensione del file, sul tipo di file, ecc.

Quando dici

find a b c

si chiede finddi elencare tutti i nomi sotto i tre percorsi a, be c. Se si tratta di nomi di file regolari nella directory corrente, verranno restituiti. Se qualcuno di loro è il nome di una directory, verrà restituito insieme a tutti gli altri nomi all'interno di quella directory.

Quando io faccio

find . -type f -name 'filename'

Questo genera un elenco di tutti i nomi nella directory corrente ( .) e in basso. Quindi limita i nomi a quelli dei normali file, cioè non alle directory ecc., Con -type f. Quindi c'è un'ulteriore limitazione ai nomi che corrispondono a filenameusando -name 'filename'. La stringa filenamepuò essere un modello globbing di nome file, ad esempio *.txt(ricordati di citare!).

Esempio:

Quanto segue sembra "trovare" il file chiamato .profilenella mia home directory:

$ pwd
/home/kk
$ find .profile
.profile

Ma in effetti, restituisce solo tutti i nomi nel percorso .profile(c'è solo un nome, e questo è di questo file).

Quindi cdsalgo di un livello e riprovo:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

Il findcomando non può ora trovare alcun percorso chiamato .profile.

Tuttavia, se riesco a esaminare la directory corrente e quindi limitare solo i nomi restituiti.profile , lo trova anche da lì:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenameritornerebbe solo filenamese filenamenon fosse di tipo directory (o fosse di tipo directory, ma non avesse alcuna voce in sé)
Stéphane Chazelas,

2

Spiegazione non tecnica: cercare Jack in mezzo alla folla è più veloce che cercare tutti in mezzo alla folla ed eliminare tutto dalla considerazione tranne Jack.


Il problema è che l'OP si aspetta che Jack sia l'unica persona nella folla. Se lo è, sono fortunati. find jackelencherà jackse si tratta di un file chiamato jacko di tutti i nomi nella directory se è una directory. È un fraintendimento di come findfunziona.
Kusalananda

1

Non ho ancora capito il problema, ma posso fornire ulteriori approfondimenti.

Come per Kusalananda, la find | grepchiamata è chiaramente più veloce sul mio sistema, il che non ha molto senso. All'inizio ho assunto una sorta di problema di buffering; che la scrittura sulla console rallenta il tempo alla successiva syscall per la lettura del nome del file successivo. La scrittura su una pipe è molto veloce: circa 40 MiB / s anche per scritture a 32 byte (sul mio sistema piuttosto lento; 300 MiB / s per una dimensione del blocco di 1 MiB). Ho quindi ipotizzato che sia in findgrado di leggere più velocemente dal file system quando si scrive su una pipe (o un file) in modo che le due operazioni di lettura dei percorsi dei file e scrittura sulla console possano essere eseguite in parallelo (cosa findche un processo a thread singolo non può fare da solo.

È findcolpa sua

Confronto tra le due chiamate

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

e

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

mostra che findfa qualcosa di incredibilmente stupido (qualunque cosa possa essere). Risulta semplicemente incompetente nell'esecuzione -name '*.txt'.

Potrebbe dipendere dal rapporto input / output

Potresti pensare che find -namevince se c'è molto poco da scrivere. Ma è solo più imbarazzante per find. Si perde anche se non c'è nulla da scrivere su file da 200K (13M di dati pipe) per grep:

time find /usr -name lwevhewoivhol

findpuò essere veloce come grep, però

Si scopre che findla stupidità con namenon si estende ad altri test. Usa invece una regex e il problema non c'è più:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Immagino che questo possa essere considerato un bug. Qualcuno disposto a presentare una segnalazione di bug? La mia versione è find (GNU findutils) 4.6.0


Quanto sono ripetibili i tuoi tempi? Se hai eseguito -nameprima il test, potrebbe essere stato più lento a causa del contenuto della directory non memorizzato nella cache. (Durante i test -namee -regextrovo che prendano all'incirca lo stesso tempo, almeno una volta che l'effetto cache è stato preso in considerazione. Naturalmente potrebbe essere solo una versione diversa di find...)
psmears,

@psmears Certo, ho fatto questi test più volte. Il problema di memorizzazione nella cache è stato menzionato anche nei commenti alla domanda prima della prima risposta. La mia findversione è find (GNU findutils) 4.6.0
Hauke ​​Laging,

Perché è sorprendente che l'aggiunta -name '*.txt'rallenti find? Deve fare un lavoro extra, testando ogni nome di file.
Barmar,

@Barmar Da un lato questo lavoro extra può essere svolto in modo estremamente rapido. D'altra parte questo lavoro extra consente di risparmiare altro lavoro. finddeve scrivere meno dati. E scrivere su una pipe è un'operazione molto più lenta.
Hauke ​​Laging,

Scrivere su un disco è molto lento, scrivere su una pipe non è poi così male, si copia solo su un buffer del kernel. Si noti che nel primo test, scrivere di più in /dev/nullqualche modo ha utilizzato meno tempo di sistema.
Barmar,

0

Avviso : suppongo che intendi find . -name filename(altrimenti, stai cercando cose diverse; in find filenamerealtà cerca un percorso chiamato nomefile , che potrebbe non contenere quasi nessun file, quindi uscire molto rapidamente).


Supponiamo di avere una directory contenente cinquemila file. Sulla maggior parte dei filesystem, questi file sono effettivamente archiviati in una struttura ad albero , che consente di individuare rapidamente un dato file.

Così, quando si chiede finddi individuare un file il cui nome richiede solo il controllo, findsi chiederà per quel file e solo quel file, per il file system sottostante, che leggerà poche pagine dalla memoria di massa. Quindi, se il filesystem vale il suo sale, questa operazione verrà eseguita molto più velocemente che attraversare l'intero albero per recuperare tutte le voci.

Quando chiedi chiaramente, findma è esattamente quello che fai, attraversi l'intero albero, leggendo. Ogni. Singolo. Iscrizione. Con directory di grandi dimensioni, questo potrebbe essere un problema (è esattamente il motivo per cui diversi software, che hanno bisogno di archiviare molti file su disco, creeranno "alberi di directory" in profondità due o tre componenti: in questo modo, ogni singola foglia deve contenere solo meno File).


-2

Supponiamo che il file / john / paul / george / ringo / beatles esista e il file che stai cercando si chiama 'pietre'

find / stones

find confronterà 'beatles' con 'stones' e lo lascerà cadere quando 's' e 'b' non corrispondono.

find / | grep stones

In questo caso find passerà '/ john / paul / george / ringo / beatles' a grep e grep dovrà farsi strada attraverso l'intero percorso prima di determinare se è una corrispondenza.

grep sta quindi facendo molto più lavoro ed è per questo che richiede più tempo


1
Ci hai provato?
Hauke ​​Laging,

3
Il costo dei confronti di stringhe (estremamente semplice ed economico) è completamente ridotto dal costo di IO (o semplicemente syscall se memorizzato nella cache) delle ricerche di directory.
Mat

grep non è un confronto di stringhe, il suo confronto di espressioni regolari che significa che deve farsi strada attraverso l'intera stringa fino a quando non trova una corrispondenza o raggiunge la fine. Le ricerche di directory sono uguali, qualunque cosa accada.
Paranoid,

@Paranoid Hm, di quale versione di find stai parlando? Apparentemente non è niente di simile alla scoperta a cui sono abituato Debian.
pipe
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.