Grep sucht zwei Wörter in einer Zeile

46

Ich habe versucht, eine Zeile zu filtern, in der die Wörter "Zitrone" und "Reis" vorkommen. Ich weiß, wie man "Zitrone" oder "Reis" findet, aber nicht die beiden. Sie müssen nicht nebeneinander stehen, sondern nur dieselbe Textzeile.

Sebastian
quelle
1
Um alle Zeichenfolgen in einer Datei zu finden, können Sie die grep-Schleife in FOR ausführen
Noam Manos

Antworten:

62

"Beide in derselben Zeile" bedeutet "Reis", gefolgt von zufälligen Zeichen, gefolgt von "Zitrone" oder umgekehrt ".

In Regex ist das rice.*lemonoder lemon.*rice. Sie können dies kombinieren mit |:

grep -E 'rice.*lemon|lemon.*rice' some_file

Wenn Sie normale reguläre Ausdrücke anstelle von erweiterten Ausdrücken ( -E) verwenden möchten, müssen Sie vor dem |folgenden Schrägstrich einen umgekehrten Schrägstrich einfügen :

grep 'rice.*lemon\|lemon.*rice' some_file

Für mehr Wörter, die schnell etwas länger werden und es normalerweise einfacher ist, mehrere Aufrufe von zu verwenden grep, zum Beispiel:

grep rice some_file | grep lemon | grep chicken
Florian Diesch
quelle
Deine letzte Zeile ist eine Konjunktion, keine Disjunktion ? Das heißt: das grep ricefindet Zeilen mit rice. Es wird eingespeist, in grep lemondie nur zitronenhaltige Zeilen gelangen. Während der OP - sowie Ihre vorherigen Antworten - erlaubt jeder von [Reis | Zitrone | Huhn]
javadba
@Florian Diesch - Darf ich erklären, warum |man entkommen muss grep? Vielen Dank!
Flüchtling
1
@fugitive egrepverwendet erweiterte reguläre Ausdrücke, wobei dies |als ODER-Logik verstanden wird. grepDer \|
Standardwert
Wie in grepder Manpage von angegeben, egrepist veraltet und sollte durch ersetzt werden grep -E. Ich habe mir die Freiheit genommen, die Antwort entsprechend zu bearbeiten.
Dessert
26

Sie können die Ausgabe des ersten grep-Befehls an einen anderen grep-Befehl leiten, der beiden Mustern entspricht. Sie können also Folgendes tun:

grep <first_pattern> <file_name> | grep <second_pattern>

oder,

cat <file_name> | grep <first_pattern> | grep <second_pattern>

Beispiel:

Fügen wir unserer Datei einige Inhalte hinzu:

$ echo "This line contains lemon." > test_grep.txt
$ echo "This line contains rice." >> test_grep.txt
$ echo "This line contains both lemon and rice." >> test_grep.txt
$ echo "This line doesn't contain any of them." >> test_grep.txt
$ echo "This line also contains both rice and lemon." >> test_grep.txt

Was enthält die Datei:

$ cat test_grep.txt 
This line contains lemon.
This line contains rice.
This line contains both lemon and rice.
This line doesn't contain any of them.
This line also contains both rice and lemon.

Lassen Sie uns nun sagen, was wir wollen:

$ grep rice test_grep.txt | grep lemon
This line contains both lemon and rice.
This line also contains both rice and lemon.

Wir erhalten nur die Linien, bei denen beide Muster übereinstimmen. Sie können dies erweitern und die Ausgabe für weitere "AND" -Matches an einen anderen grep-Befehl leiten.

Aditya
quelle
21

Obwohl die Frage nach "grep" fragt, dachte ich, dass es hilfreich sein könnte, eine einfache "awk" -Lösung zu posten:

awk '/lemon/ && /rice/'

Dies kann leicht mit mehr Wörtern oder anderen booleschen Ausdrücken außer 'und' erweitert werden.

David B.
quelle
11

Eine andere Idee, um die Übereinstimmungen in beliebiger Reihenfolge zu finden, ist:

grep mit -P (Perl-Compatibility) Option und positivem Lookahead Regex(?=(regex)) :

grep -P '(?=.*?lemon)(?=.*?rice)' infile

oder Sie können stattdessen unten verwenden:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • Das .*?bedeutet, dass alle Zeichen gefunden werden ., die nicht oder mehrmals vorkommen, *wenn sie optional sind, gefolgt von einem Muster ( riceoder lemon). Das ?macht alles optional davor (bedeutet null oder einmal von allem zusammengepasst .*)

(?=pattern): Positiver Lookahead: Das positive Lookahead-Konstrukt besteht aus zwei Klammern, wobei die öffnende Klammer von einem Fragezeichen und einem Gleichheitszeichen gefolgt wird.

Dies gibt also alle Zeilen mit sowohl lemonals auch ricein zufälliger Reihenfolge zurück. Auch dies vermeidet die Verwendung von |s und doppelten greps.


Externe Links: Erweiterte Grep-Themen Positive Lookahead - GREP für Designer

αғsнιη
quelle
5
grep -e foo -e goo

Gibt Streichhölzer für Foo oder Goo zurück

Netzknick
quelle
1

Wenn wir zugeben, dass es grepakzeptabel ist, eine Antwort zu liefern, die nicht auf der obigen Antwort basiert awk, würde ich eine einfache perlZeile vorschlagen , wie:

$ perl -ne 'print if /lemon/ and /rice/' my_text_file

Die Suche kann Groß- / Kleinschreibung mit einigen / allen Wörtern ignorieren /lemon/i and /rice/i. Auf den meisten Unix / Linux-Rechnern ist Perl genauso installiert wie awk.

Gilles Maisonneuve
quelle
Verweigert!!! ;) Weil es keinen Sinn macht .. :)
An0n
0

Hier ist ein Skript zur Automatisierung der grep-Piping-Lösung:

#!/bin/bash

# Use filename if provided as environment variable, or "foo" as default
filename=${filename-foo}

grepand () {
# disable word splitting and globbing
IFS=
set -f
if [[ -n $1 ]]
then
grep -i "$1" ${filename} | filename="" grepand "${@:2}"
else
# If there are no arguments, assume last command in pipe and print everything
cat
fi
}

grepand "$@"
Jeff
quelle
1
Dies sollte wahrscheinlich mithilfe einer rekursiven Funktion implementiert werden, anstatt eine Befehlszeichenfolge zu eval
erstellen
@muru Fühlen Sie sich frei, eine Bearbeitung vorzuschlagen. Ich schätze den Kommentar.
Jeff
1
Wenn ich das hier bearbeite, ist das zu viel für ein Umschreiben, also werde ich das nicht tun. Wenn Sie es hinzufügen möchten, ist hier, wie ich es mir vorstelle, aussehen sollte: paste.ubuntu.com/23915379
muru