wake-up-neo.net

Come posso grep per più pattern con pattern con un carattere pipe?

Voglio trovare tutte le righe in diversi file che corrispondono a uno dei due modelli. Ho provato a trovare gli schemi che sto cercando digitando

grep (foo|bar) *.txt

ma Shell interpreta il | come pipe e si lamenta quando bar non è un eseguibile.

Come posso grep per più pattern nello stesso set di file?

681
Dan

Innanzitutto, è necessario proteggere il modello dall'espansione di Shell. Il modo più semplice per farlo è quello di racchiuderlo tra virgolette singole. Le virgolette singole impediscono l'espansione di qualsiasi cosa tra loro (comprese le barre rovesciate); l'unica cosa che non puoi fare allora è avere singole virgolette nel modello.

grep -- 'foo*' *.txt

(nota anche il -- marker di fine opzione per interrompere alcune implementazioni di grep tra cui GNU grep dal trattamento di un file chiamato -foo-.txt ad esempio (che sarebbe espanso da Shell da *.txt) da prendere come opzione (anche se segue un argomento non opzionale)).

Se hai bisogno di una sola citazione, puoi scriverla come '\'' (fine stringa letterale, citazione letterale, stringa aperta letterale).

grep -- 'foo*'\''bar' *.txt

In secondo luogo, grep supporta almeno¹ due sintassi per i pattern. La vecchia sintassi predefinita ( espressioni regolari di base ) non supporta l'alternanza (|) operatore, sebbene alcune versioni lo abbiano come estensione, ma scritto con una barra rovesciata.

grep -- 'foo\|bar' *.txt

Il modo portatile è usare la sintassi più recente, espressioni regolari estese . Devi passare il -E opzione su grep per selezionarlo (precedentemente fatto con il comando separato egrep )²)

grep -E -- 'foo|bar' *.txt

Un'altra possibilità quando stai solo cercando uno dei tanti pattern (invece di costruire un pattern complesso usando la disgiunzione) è passare più pattern a grep. Puoi farlo precedendo ogni modello con il -e opzione.

grep -e foo -e bar -- *.txt

Oppure metti i motivi su più linee:

grep -- 'foo
bar' *.txt

O memorizza quei modelli in un file, uno per riga ed esegui

grep -f that-file -- *.txt

Nota che se *.txt si espande in un singolo file, grep non anteporrà le righe corrispondenti con il suo nome come accade quando sono presenti più file. Per ovviare a questo, con alcune grep implementazioni come GNU grep, puoi usare -H opzione o con qualsiasi implementazione, puoi passare /dev/null come argomento aggiuntivo.


¹ alcune implementazioni grep supportano ancora di più come quelle compatibili con Perl con -P o aumentato quelli con -X, -K per i caratteri jolly ksh ...

² mentre egrep è stato deprecato da POSIX e talvolta non viene più trovato su alcuni sistemi, su altri sistemi come Solaris quando POSIX o GNU non sono state installate, quindi egrep è la tua unica opzione in quanto /bin/grep non supporta nessuno di -e, -f, -E, \| o schemi multilinea

egrep "foo|bar" *.txt

o

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

citando selettivamente la pagina man di gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: “basic” and “extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

All'inizio non ho letto più, quindi non ho riconosciuto le sottili differenze:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

Ho sempre usato egrep e inutilmente le parentesi, perché ho imparato dagli esempi. Ora ho imparato qualcosa di nuovo. :)

109
user unknown

Come diceva TC1, -F sembra essere un'opzione utilizzabile:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

Innanzitutto, è necessario utilizzare le virgolette per caratteri speciali. Secondo, anche così, grep non capirà direttamente l'alternanza; dovresti usare egrep, oppure (con GNU grep) grep -E.

egrep 'foo|bar' *.txt

(Le parentesi non sono necessarie a meno che l'alternanza non faccia parte di una regex più grande.)

17
geekosaur

Se non hai bisogno di espressioni regolari, è molto più veloce usare fgrep o grep -F con più parametri -e, in questo modo:

fgrep -efoo -ebar *.txt

fgrep (in alternativa grep -F) è molto più veloce del grep normale perché cerca stringhe fisse anziché espressioni regolari.

8

Puoi provare il comando seguente per ottenere il risultato:

egrep 'rose.*Lotus|lotus.*rose' some_file
6
Abhishek

Tubo (|) è un personaggio Shell speciale, quindi deve essere salvato (\|) o citato come da manuale ( man bash ):

Le virgolette vengono utilizzate per rimuovere il significato speciale di determinati caratteri o parole in Shell. Può essere utilizzato per disabilitare il trattamento speciale per caratteri speciali, per impedire che le parole riservate vengano riconosciute come tali e per impedire l'espansione dei parametri.

Racchiudere i caratteri tra virgolette doppie conserva il valore letterale di tutti i caratteri tra virgolette

Una barra rovesciata non quotata (\) è il carattere di escape.

Vedi: Quali personaggi devono essere evasi in Bash?

Ecco alcuni esempi (utilizzando strumenti non ancora menzionati):

  • Utilizzando ripgrep :

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Utilizzando git grep :

    • git grep --no-index -e foo --or -e bar

      Nota: supporta anche espressioni booleane come --and, --or e --not.

Per l'operazione AND per riga, vedere: Come eseguire grep con più pattern AND?

Per l'operazione AND per file, vedere: Come controllare tutte le stringhe o regex multiple presenti in un file?

4
kenorb

Un modo economico e allegro per sostenere più schemi:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq
3
DHDHDHD

Avevo registri di accesso in cui le date erano stupidamente formattate: [30/giu/2013: 08: 00: 45 +0200]

Ma dovevo visualizzarlo come: 30/Jun/2013 08:00:45

Il problema è che usando "OR" nella mia dichiarazione grep, stavo ricevendo le due espressioni di corrispondenza su due righe separate.

Ecco la soluzione:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log
3
tsmets

TL; DR: se vuoi fare più cose dopo aver abbinato uno dei molteplici pattern, racchiudili come in \(pattern1\|pattern2\)

esempio: voglio trovare tutti i luoghi in cui una variabile che contiene il nome 'data' è definita come una stringa o int. (ad es. "int cronDate =" o "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

Con grep -E, Non è necessario uscire dalle parentesi o dalla pipe, ovvero grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='

2
jeremysprofile

Questo funziona per me

[email protected]:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing [email protected]:/home/sshuser#
1
Mansur Ali

Esistono diversi modi per farlo.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

La terza e la quarta opzione verranno visualizzate solo nei file ed eviteranno che le directory abbiano .txt nei loro nomi.
Quindi, come per il tuo caso d'uso, puoi utilizzare una delle opzioni sopra menzionate.
Grazie!!

1

da aggiungere a @ geekosaur's answer , se hai più pattern che contengono anche tab e spazio, usa il seguente comando

grep -E "foo[[:blank:]]|bar[[:blank:]]"

dove [[:blank:]] è una classe di caratteri RE che rappresenta uno spazio o un carattere di tabulazione

1
Fuseteam