Ottimizzazione di GNU grep


8

Sto usando egrep ( grep -E) con un file PATTERN. ( -f path/to/file).

Questo viene fatto in un ciclo infinito su un flusso di testo. Ciò implica che non posso accumulare e passare TUTTI gli input a grep in una sola volta (come *.log).

C'è un modo per fare in modo che grep "salvi" l'NFA che sta costruendo dal file PATTERN da usare per la sua prossima esecuzione?

Ho cercato su Google e ho letto la documentazione senza fortuna.

Proverò a spiegarlo un po 'di più. Devo individuare un numero fisso di stringhe con regex (questa non è una parte di una domanda ma non posso suggerire diversamente) come indirizzi IP, domini ecc. La ricerca viene effettuata su un feed da Internet. Puoi pensarlo come un flusso di testo. Non riesco a utilizzare greptutti gli input poiché è uno stream. Posso accumulare un pezzo di stream e usarlo grepsu di esso (quindi non usando grepsu ogni riga) ma anche questo è limitato (diciamo per 30 secondi).

So che grepsta costruendo un NFA da tutti i suoi schemi (nel mio caso da un file). Quindi la mia domanda qui è: posso dire grepdi salvare quell'NFA per la prossima corsa, dal momento che non cambierà? Ciò mi farebbe risparmiare il tempo di costruire quell'NFA ogni volta.


Cosa intendi con Questo viene fatto in un ciclo infinito su un flusso di testo ? Stai dicendo che ne stai eseguendo uno grepper riga di testo? Da dove viene il testo? Sarebbe tail -fun'opzione?
Stéphane Chazelas,

Diciamo che sto accumulando il flusso per 30 secondi e quindi eseguo grepsu quel pezzo.
Bergerg,

1
Non è ancora chiaro il motivo per cui dovresti correre greppiù volte. Possibilmente correlato: perché l'abbinamento delle stringhe 1250 con modelli da 90k è così lento?
Stéphane Chazelas,

5
grepè pensato per funzionare su un flusso di testo, non capisco ancora perché dovresti eseguire diverse istanze. Perché non puoi alimentare tutti quelli nella stessa grepistanza? Perché è necessario accumularli prima di nutrirsi grep?
Stéphane Chazelas,

2
Dai un'occhiata a flex e scrivi il tuo programma, che potrebbe rivelarsi molto più veloce.
user2064000

Risposte:


14

No, non esiste nulla del genere. Generalmente il costo di avvio grep(fork di un nuovo processo, caricamento dell'eseguibile, libreria condivisa, collegamento dinamico ...) sarebbe molto maggiore rispetto alla compilazione delle regexps, quindi questo tipo di ottimizzazione avrebbe poco senso.

Tuttavia, vedi Perché l'abbinamento delle stringhe 1250 con modelli da 90k è così lento? su un bug in alcune versioni di GNU grepche lo renderebbe particolarmente lento per un gran numero di regexps.

Forse qui, potresti evitare di correre greppiù volte alimentando i tuoi pezzi nella stessa grepistanza, ad esempio usandolo come coprocesso e usando un marcatore per rilevare la fine. Con zshe GNU grepe awkimplementazioni diverse da mawk:

coproc grep -E -f patterns -e '^@@MARKER@@$' --line-buffered
process_chunk() {
  { cat; echo @@MARKER@@; } >&p & awk '$0 == "@@MARKER@@"{exit};1' <&p
}
process_chunk < chunk1 > chunk1.grepped
process_chunk < chunk2 > chunk2.grepped

Anche se può essere più semplice fare tutto con awko perlinvece.

Ma se non hai bisogno che l' grepoutput vada in file diversi per blocchi diversi, puoi sempre fare:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output

Ho veraion 3+ di grep, quindi non è questo il problema. Non ho nemmeno considerato il bivio. Immagino che proverò a trasmettere tutto così grepcom'è. Grazie.
Bergerg,

L'eseguibile e le librerie condivise rimarrebbero nei buffer RAM dopo la terminazione dei processi (a meno che l'OP non abbia effettivamente poca RAM)?
Dmitry Grigoryev l'

2
@DmitryGrigoryev, sì, molto probabilmente, deve ancora essere mappato nello spazio degli indirizzi del processo ed eseguire la modifica del collegamento. C'è di più come caricare e analizzare i dati delle impostazioni locali, analizzare le opzioni, l'ambiente ... Il punto è che il costo di regcomp () è diluito in tutto quel sovraccarico. La prima cosa da fare quando si ottimizza sarebbe innanzitutto evitare di eseguire diverse greps.
Stéphane Chazelas,

1

Non posso usare grep su tutti gli input dato che è uno stream. Posso accumulare un pezzo di stream e usare grep su di esso ...

Sei a conoscenza del blocco delle condutture? Se si esegue il pipe di grep e tutto l'input non è disponibile, grep attenderà fino a quando non sarà disponibile e quindi continuerà come se l'input fosse sempre presente.

$ ( echo a1; echo b1; sleep 5; echo a2 ) | grep 'a.'
a1
a2

EDIT: Il modo in cui funzionano le pipeline, ad esempio, cmd1 | cmd2è che entrambi i programmi verranno avviati contemporaneamente, con un "buffer di blocchi" di 65.536 byte, ad esempio. Quando cmd2tenta di leggere e quel buffer è vuoto, attenderà la disponibilità di un blocco. Quando cmd1tenta di scrivere e quel buffer è pieno, attenderà fino a quando cmd2non lo legge.

Da quello che posso leggere, non è necessario tagliare l'input in blocchi e passarli a grep separatamente. È già fatto automaticamente.

EDIT2: grepdovrebbe anche stampare i risultati non appena li trova nello stream. Non è necessario che lo stream termini prima di poter ottenere i risultati.


0

Forse puoi "usare grep su tutti gli input"? Usando nc(netcat), o attraverso scripto tramite altri strumenti simili? Soprattutto se il tuo file di pattern è di dimensioni gestibili (diciamo meno di 1000 regexps).

Primo esempio : è possibile utilizzare egrepuna connessione di streaming: (qui è mostrato l'esempio con nc, ma altri potrebbero applicare)

prompt:/some/path $ nc somehost someport | egrep -f patternfile | gzip -c - > results.gz

# and while this is running, you can have a look at the growing results.gz:
prompt:/some/otherpath $ tail -f /some/path/results.gz | gzip -c - | less

(nota: puoi anche: touch /some/path/results.gzprima di avviare il nccomando e avere tail -fsu quel file (vuoto) per non perdere nulla. Comunque, results.gz conterrà tutto ciò che volevi catturare)

secondo esempio : potresti anche egrepsu una sessione di shell attualmente in esecuzione (e mostrando un altro modo per seguire la progressione):

#in 1 terminal:
prompt:/home/userA $ script
Script command is started. The file is typescript.
prompt:/home/userA $ 
 ... doing here whatever you want (start IRC? etc) ...
prompt:/home/userA $ ctrl-d # to end the current script session
Script command is complete. The file is typescript.

#and in another terminal, while you are "doing here whatever you want" :
prompt:/home/somewhere $ tail -f /home/userA/typescript | egrep -f patternfile  | tee /some/place/to/store/results.gz

egrepè una versione altamente efficiente di grep, sulla maggior parte dei sistemi (vedere alcune informazioni interessanti su: https://swtch.com/~rsc/regexp/regexp1.html )


potresti persino usare l'esempio1 su cose come un'uscita dd, ecc.
Olivier Dulac,

nota a margine interessante: grep è più efficiente quanto più grande è la parte nota di regexp (es: cercare la stringa o regexp sè molto, poltare più lentamente della corrispondenza somethinge questo è molto più lento della corrispondenza something even much longer(quest'ultima consente alla partita regexp di saltare più grande) porzioni dell'input quando differiscono). Su file di grandi dimensioni, fondamentalmente "divide" il tempo per analizzarlo per il rapporto di lunghezza (cioè, il grepping di un personaggio noto è quasi 40 volte più lento rispetto alla corrispondenza di una stringa di 40 caratteri noti. prof. ma è davvero evidente.)
Olivier Dulac l'
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.