Wie greife ich nach mehreren Mustern mit einem Pipe-Zeichen?


624

Ich möchte alle Zeilen in mehreren Dateien finden, die einem von zwei Mustern entsprechen. Ich habe versucht, die Muster, nach denen ich suche, durch Tippen zu finden

grep (foo|bar) *.txt

Aber die Shell interpretiert das |als Pipe und beschwert sich, wenn bares sich nicht um eine ausführbare Datei handelt.

Wie kann ich in einem Satz von Dateien nach mehreren Mustern suchen?



grep 'word1 \ | word2 \ | word3' / path / to / file
lambodar

Antworten:


861

Zunächst müssen Sie das Muster vor der Expansion durch die Shell schützen. Der einfachste Weg, dies zu tun, besteht darin, einfache Anführungszeichen zu setzen. Einfache Anführungszeichen verhindern die Erweiterung von Elementen (einschließlich umgekehrter Schrägstriche). Das einzige, was Sie dann nicht tun können, sind einfache Anführungszeichen im Muster.

grep 'foo*' *.txt

Wenn Sie ein einfaches Anführungszeichen benötigen, können Sie es als '\''(Endzeichenfolgenliteral, Literalzitat, offenes Zeichenfolgenliteral) schreiben .

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

Zweitens unterstützt grep zwei Syntaxen für Muster. Die alte Standardsyntax ( grundlegende reguläre Ausdrücke ) unterstützt den |Operator alternation ( ) nicht, obwohl er in einigen Versionen als Erweiterung, jedoch mit einem Backslash geschrieben ist.

grep 'foo\|bar' *.txt

Der portable Weg ist, die neuere Syntax zu verwenden, erweiterte reguläre Ausdrücke . Sie müssen die -EOption übergeben grep, um es auszuwählen. Unter Linux können Sie auch egrepanstelle von grep -Eeingeben (bei anderen Unices können Sie dies als Alias ​​festlegen).

grep -E 'foo|bar' *.txt

Eine andere Möglichkeit, wenn Sie nur nach einem von mehreren Mustern suchen (im Gegensatz zum Erstellen eines komplexen Musters mit Disjunktion), besteht darin, mehrere Muster an zu übergeben grep. Sie können dies tun, indem Sie jedem Muster die -eOption voranstellen .

grep -e foo -e bar *.txt

18
Als Nebenbemerkung: Wenn die Muster fixiert sind, sollten Sie sich wirklich an die Gewohnheit gewöhnen, fgrepoder grep -Fbei kleinen Mustern wird der Unterschied vernachlässigbar sein, aber wenn sie länger werden, zeigen sich die Vorteile ...
TC1

7
@ TC1 fgrep ist laut Manpage
RAMN

18
@ TC1 Ob grep -Ftatsächlich ein Leistungsvorteil erzielt wird, hängt von der grep-Implementierung ab: Einige von ihnen wenden sowieso denselben Algorithmus an, sodass sich -Fdies nur auf die Zeit auswirkt, die für das Parsen des Musters aufgewendet wird, und nicht auf die Zeit für die Suche. GNU grep ist -Fzum Beispiel mit nicht schneller (es hat auch einen Fehler, der grep -Fin Multibyte-Gebietsschemata langsamer macht - dasselbe konstante Muster mit grepist tatsächlich bedeutend schneller!). Auf der anderen Seite profitiert BusyBox grep sehr von -Fgroßen Dateien.
Gilles

4
Vielleicht sollte erwähnt werden, dass bei komplizierteren Mustern, bei denen die Abwechslung nur für einen Teil des regulären Ausdrucks gelten soll, diese mit "\ (" und "\)" gruppiert werden kann (das Escaping gilt für die standardmäßigen "regulären Grundausdrücke"). ) (?).
Peter Mortensen

4
Beachten Sie, dass egrepälter ist grep -E. Es ist nicht GNU-spezifisch (es hat sicherlich nichts mit Linux zu tun). Tatsächlich gibt es immer noch Systeme wie Solaris, bei denen die Standardeinstellung grepimmer noch nicht unterstützt wird -E.
Stéphane Chazelas

90
egrep "foo|bar" *.txt

oder

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

selektives Zitieren der Manpage von 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.

Am Anfang habe ich nicht weiter gelesen, also habe ich die subtilen Unterschiede nicht erkannt:

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

Ich habe immer egrep und unnötigerweise parens verwendet, weil ich aus Beispielen gelernt habe. Jetzt habe ich etwas Neues gelernt. :)


22

Wie TC1 sagte, -Fscheint die Option verwendbar zu sein:

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

$> patterns="foo
bar" 

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

1
@poige Ich wusste nichts über die Option $ 'foo \ nbar', bin mir nicht sicher, wie die Erweiterung hier funktioniert, muss nachschlagen, aber danke, das ist wirklich nützlich.
Haridsv

Nett! Diese Option scheint auch die Ausführung zu beschleunigen (da reguläre Ausdrücke deaktiviert werden).
Qwertzguy

15

Erstens müssen Sie Anführungszeichen für Sonderzeichen verwenden. Zweitens grepwird der Wechsel trotzdem nicht direkt verstanden; Sie müssten egrepoder (nur mit GNU grep) verwenden grep -E.

egrep 'foo|bar' *.txt

(Die Klammern sind nicht erforderlich, es sei denn, die Abwechslung ist Teil eines größeren regulären Ausdrucks.)


4
Eigentlich grep -Eist mehr Standard als egrep.
JW013

8

Wenn Sie keine regulären Ausdrücke benötigen, können Sie diese viel schneller fgrepoder grep -Fmit mehreren -e-Parametern verwenden:

fgrep -efoo -ebar *.txt

fgrep(alternativ grep -F) ist viel schneller als reguläres grep, da nach festen Zeichenfolgen anstatt nach regulären Ausdrücken gesucht wird.


4
Bitte beachten Sie auch die Kommentare auf dieser Seite, in denen darauf fgrephingewiesen wird , dass dies veraltet ist.
Phk

6

Sie können den folgenden Befehl verwenden, um das Ergebnis zu erhalten:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Eine billige und fröhliche Art, nach mehreren Mustern zu suchen:

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

Es könnte von einer Erklärung profitieren.
Peter Mortensen

2
Die Erklärung ist, dass die -fOption von grep eine Datei mit mehreren Mustern akzeptiert. Anstatt eine temporäre Datei zu erstellen (die Sie möglicherweise später nicht löschen möchten), verwenden Sie einfach die Prozessersetzung der Shell:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Pipe ( |) ist ein spezielles Shell-Zeichen, daher muss es entweder mit Escape-Zeichen versehen ( \|) oder in Anführungszeichen gesetzt werden ( man bash):

Das Zitieren wird verwendet, um die spezielle Bedeutung bestimmter Zeichen oder Wörter in der Shell zu entfernen . Sie können die Sonderbehandlung für Sonderzeichen deaktivieren, verhindern, dass reservierte Wörter als solche erkannt werden, und die Parametererweiterung verhindern.

Wenn Sie Zeichen in doppelte Anführungszeichen setzen, bleibt der Literalwert aller Zeichen in den Anführungszeichen erhalten

Ein Backslash ( \) ohne Anführungszeichen ist das Escape-Zeichen.

Siehe: Welche Zeichen müssen in Bash maskiert werden?

Hier sind einige Beispiele (mit Tools, die noch nicht erwähnt wurden):

  • Verwenden von ripgrep:

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

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

      Hinweis: Es werden auch boolesche Ausdrücke wie --and, --orund unterstützt --not.

Informationen zur UND-Verknüpfung pro Zeile finden Sie unter: Ausführen von grep mit mehreren UND-Mustern.

Informationen zur UND-Verknüpfung pro Datei finden Sie unter: So überprüfen Sie, ob in einer Datei mehrere Zeichenfolgen oder reguläre Ausdrücke vorhanden sind.


3

Ich hatte Zugriffsprotokolle, in denen die Daten dumm formatiert waren: [30 / Jun / 2013: 08: 00: 45 +0200]

Aber ich musste es anzeigen als: 30.06.2013 08:00:45

Das Problem ist, dass ich mit "OR" in meiner grep-Anweisung die beiden Übereinstimmungsausdrücke in zwei separaten Zeilen erhalten habe.

Hier ist die Lösung:

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

2

TL; DR: Wenn Sie nach dem Abgleichen eines der mehreren Muster weitere Schritte ausführen möchten, fügen Sie diese wie in ein \(pattern1\|pattern2\)

Beispiel: Ich möchte alle Stellen finden, an denen eine Variable, die den Namen 'Datum' enthält, als String oder int definiert ist. (zB "int cronDate =" oder "String textFormattedDateStamp ="):

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

Mit grep -Ebrauchen Sie weder die Klammern noch die Pipe zu verlassen, dhgrep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Das funktioniert bei mir

root@gateway:/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 root@gateway:/home/sshuser#

1

Hierfür gibt es mehrere Möglichkeiten.

  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'

Die 3. und 4. Option greift nur auf die Dateien zu und vermeidet, dass Verzeichnisse .txtin ihren Namen enthalten sind.
Entsprechend Ihrem Anwendungsfall können Sie also eine der oben genannten Optionen verwenden.
Vielen Dank!!


0

Um die Antwort von @geekosaur zu ergänzen, verwenden Sie den folgenden Befehl, wenn Sie mehrere Muster haben, die auch Tabulatoren und Leerzeichen enthalten

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

Dabei [[:blank:]]ist RE eine Zeichenklasse, die entweder ein Leerzeichen oder ein Tabulatorzeichen darstellt

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.