it-swarm.com.de

Verbinde zwei Saiten in einer Zeile mit grep

Ich versuche, grep zu verwenden, um Zeilen abzugleichen, die zwei verschiedene Zeichenfolgen enthalten. Ich habe Folgendes versucht, aber dies stimmt mit Zeilen überein, die entweder string1oder string2 enthalten, was ich nicht will.

grep 'string1\|string2' filename

Wie kann ich also mit grep nur die Zeilen abgleichen, die beide Zeichenfolgen enthalten?

197
hearsaxas

Sie können grep 'string1' filename | grep 'string2'

Oder, grep 'string1.*string2\|string2.*string1' filename

171
dheerosaur

Ich denke das ist wonach du gesucht hast:

grep -E "string1|string2" filename

Ich denke das antwortet so:

grep 'string1.*string2\|string2.*string1' filename

stimmen nur mit dem Fall überein, in dem beide vorhanden sind, nicht der eine oder der andere oder beide.

195
user45949

Gib es einfach mehrfach -e Optionen.

 -e pattern, --regexp=pattern
         Specify a pattern used during the search of the input: an input
         line is selected if it matches any of the specified patterns.
         This option is most useful when multiple -e options are used to
         specify multiple patterns, or when a pattern begins with a dash
         (`-').

So wird der Befehl:

grep -e "string1" -e "string2" filename

Hinweis: Oben habe ich das Handbuch der BSD-Version zitiert, aber es sieht aus wie dasselbe unter Linux .

44
Tony

So suchen Sie überall nach Dateien, die alle Wörter in einer beliebigen Reihenfolge enthalten:

grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'

Das erste grep startet eine rekursive Suche (r), ignoriert die Groß-/Kleinschreibung (i) und listet (druckt) den Namen der Dateien auf, die mit (l) übereinstimmen ein Begriff ('action' mit den einfachen Anführungszeichen) an einer beliebigen Stelle in der Datei.

Die nachfolgenden Überprüfungen suchen nach den anderen Begriffen, wobei die Groß- und Kleinschreibung unberücksichtigt bleibt und die übereinstimmenden Dateien aufgelistet werden.

Die endgültige Liste der Dateien, die Sie erhalten, enthält diese Begriffe in beliebiger Reihenfolge an einer beliebigen Stelle in der Datei.

26
Kinjal Dixit

Wenn Sie ein grep mit einem -P Option für eine begrenzte Perl Regex, die Sie verwenden können

grep -P '(?=.*string1)(?=.*string2)'

das hat den Vorteil, mit überlappenden Strings zu arbeiten. Es ist etwas einfacher, Perl als grep zu verwenden, da Sie die und -Logik direkter angeben können:

Perl -ne 'print if /string1/ && /string2/'
14
tchrist

Ihre Methode war fast gut, nur das -w fehlte

grep -w 'string1\|string2' filename
12
Leo

Sie könnten so etwas ausprobieren:

(pattern1.*pattern2|pattern2.*pattern1)
7
Dorn

Das | Operator in einem regulären Ausdruck bedeutet oder. Das heißt, entweder string1 oder string2 stimmen überein. Du könntest es tun:

grep 'string1' filename | grep 'string2'

dadurch werden die Ergebnisse des ersten Befehls an das zweite grep weitergeleitet. Das sollte Ihnen nur Zeilen geben, die beiden entsprechen.

6
martineno

Und wie die Leute Perl und Python und verschachtelte Shell-Skripte vorgeschlagen haben, hier ein einfacher awk Ansatz:

awk '/string1/ && /string2/' filename

In den Kommentaren zu der akzeptierten Antwort nachgeschaut: nein, das geht nicht mehrzeilig; Aber das ist es auch nicht, wonach der Autor der Frage gefragt hat.

3
tink

Gefundene Zeilen, die nur mit 6 Leerzeichen beginnen und enden mit:

 cat my_file.txt | grep
 -e '^      .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
 -e '^      .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
 > nolog.txt
2
Cristian

Versuchen Sie nicht, grep zu verwenden, sondern awk. Wenn Sie 2 reguläre Ausdrücke R1 und R2 in grep zuordnen, würden Sie denken, es wäre:

grep 'R1.*R2|R2.*R1'

in awk wäre es:

awk '/R1/ && /R2/'

aber was wenn R2 überschneidet sich mit oder ist eine Teilmenge von R1? Dieser grep-Befehl würde einfach nicht funktionieren, während der awk-Befehl funktionieren würde. Nehmen wir an, Sie möchten Zeilen suchen, die the und heat enthalten:

$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre

Dazu müssten Sie 2 greps und eine Pipe verwenden:

$ echo 'theatre' | grep 'the' | grep 'heat'
theatre

und natürlich, wenn Sie tatsächlich verlangt hätten, dass sie getrennt sind, können Sie immer die gleichen regulären Ausdrücke in awk schreiben, die Sie in grep verwendet haben, und es gibt alternative awk-Lösungen, bei denen die regulären Ausdrücke nicht in jeder möglichen Reihenfolge wiederholt werden.

Abgesehen davon, was wäre, wenn Sie Ihre Lösung auf 3 reguläre Ausdrücke R1, R2 und R3 erweitern möchten. In grep wäre das eine dieser schlechten Entscheidungen:

grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3

in awk wäre es die prägnante, offensichtliche, einfache, effiziente:

awk '/R1/ && /R2/ && /R3/'

Was wäre, wenn Sie tatsächlich die Literalzeichenfolgen S1 und S2 anstelle der regulären Zeichenfolgen R1 und R2 abgleichen wollten? Sie können dies einfach nicht in einem einzigen Aufruf von grep tun. Sie müssen entweder Code schreiben, um alle RE-Metacodes zu umgehen, bevor Sie grep aufrufen:

S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'

oder nochmal 2 greps und eine Pipe:

grep -F 'S1' file | grep -F 'S2'

was wiederum schlechte Entscheidungen sind, während bei awk einfach ein String-Operator anstelle eines Regexp-Operators verwendet wird:

awk 'index($0,S1) && index($0.S2)'

Was wäre, wenn Sie zwei reguläre Ausdrücke in einem Absatz anstatt in einer Zeile suchen würden? Kann nicht in grep gemacht werden, trivial in awk:

awk -v RS='' '/R1/ && /R2/'

Wie wäre es mit einer ganzen Datei? Wiederum ist dies nicht in grep und trivial in awk möglich (diesmal verwende ich GNU awk für Multi-Char-RS aus Gründen der Übersichtlichkeit, aber es ist nicht viel mehr Code in awk, oder Sie können ihn auswählen ein Steuerzeichen, von dem Sie wissen, dass es nicht in der Eingabe enthalten ist, damit der RS ​​dasselbe tut):

awk -v RS='^$' '/R1/ && /R2/'

Wenn Sie also mehrere reguläre Ausdrücke oder Zeichenfolgen in einer Zeile, einem Absatz oder einer Datei suchen möchten, verwenden Sie nicht grep, sondern awk.

2
Ed Morton

Angenommen, wir müssen die Anzahl der Wörter in einer Datei-Testdatei ermitteln. Es gibt zwei Möglichkeiten

1) Verwenden Sie den Befehl grep mit einem Regex-Matching-Muster

grep -c '\<\(DOG\|CAT\)\>' testfile

2) Verwenden Sie den Befehl egrep

egrep -c 'DOG|CAT' testfile 

Mit egrep brauchen Sie sich keine Gedanken über den Ausdruck zu machen und nur Wörter durch ein Pipe-Trennzeichen zu trennen.

2
Amit Singh
grep ‘string1\|string2’ FILENAME 

GNU grep Version 3.1

2
tilikoom

git grep

Hier ist die Syntax unter Verwendung von git grep mit mehreren Mustern:

git grep --all-match --no-index -l -e string1 -e string2 -e string3 file

Sie können Muster auch mit Booleschen Ausdrücken wie --and, --or Und --not Kombinieren.

Überprüfen Sie man git-grep Für Hilfe.


--all-match Wenn Sie mehrere Musterausdrücke angeben, wird dieses Flag angegeben, um die Übereinstimmung auf Dateien zu beschränken, deren Zeilen mit allen übereinstimmen.

--no-indexDateien im aktuellen Verzeichnis suchen, die nicht von Git verwaltet werden.

-l/--files-with-matches/--name-only Nur die Dateinamen anzeigen.

-e Der nächste Parameter ist das Muster. Standardmäßig wird Basic Regexp verwendet.

Andere zu berücksichtigende Parameter:

--threads Anzahl der zu verwendenden grep-Worker-Threads.

-q/--quiet/--silent Keine übereinstimmenden Zeilen ausgeben; Beenden mit Status 0, wenn eine Übereinstimmung vorliegt.

Um den Mustertyp zu ändern, können Sie auch -G/--basic-regexp (Standard), -F/--fixed-strings, -E/--extended-regexp, -P/--Perl-regexp, -f file Und andere.

Verbunden:

Für [~ # ~] oder [~ # ~] siehe:

1
kenorb
grep -i -w 'string1\|string2' filename

Dies funktioniert für exakte Wortübereinstimmungen und für Wörter, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird. -I wird verwendet

1
Saurabh

Platzieren Sie die Zeichenfolgen, nach denen Sie suchen möchten, in einer Datei

echo who    > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt

Dann suchen Sie mit -f

grep -f find.txt BIG_FILE_TO_SEARCH.txt 
1
Tim Seed
grep '(string1.*string2 | string2.*string1)' filename

erhält die Zeile mit string1 und string2 in beliebiger Reihenfolge

1
James

für mehrzeilige Übereinstimmung:

echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"

oder

echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"

wir müssen nur das Zeilenumbruchzeichen entfernen und es funktioniert!

0
Aquarius Power

Ich stoße oft auf dasselbe Problem wie deins und habe gerade ein Skript geschrieben:

function m() { # m means 'multi pattern grep'

    function _usage() {
    echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
    echo "-i : ignore case"
    echo "-n : show line number"
    echo "-H : show filename"
    echo "-h : show header"
    echo "-p : specify pattern"
    }

    declare -a patterns
    # it is important to declare OPTIND as local
    local ignorecase_flag  filename linum header_flag colon result OPTIND

    while getopts "iHhnp:" opt; do
    case $opt in
        i)
        ignorecase_flag=true ;;
        H)
        filename="FILENAME," ;;
        n)
        linum="NR," ;;
        p)
        patterns+=( "$OPTARG" ) ;;
        h)
        header_flag=true ;;
        \?)
        _usage
        return ;;
    esac
    done

    if [[ -n $filename || -n $linum ]]; then
    colon="\":\","
    fi

    shift $(( $OPTIND - 1 ))

    if [[ $ignorecase_flag == true ]]; then
    for s in "${patterns[@]}"; do
            result+=" && s~/${s,,}/"
    done
    result=${result# && }
    result="{s=tolower(\$0)} $result"
    else
    for s in "${patterns[@]}"; do
            result="$result && /$s/"
    done
    result=${result# && }
    fi

    result+=" { print "$filename$linum$colon"\$0 }"

    if [[ ! -t 0 ]]; then       # pipe case
    cat - | awk "${result}"
    else
    for f in "[email protected]"; do
        [[ $header_flag == true ]] && echo "########## $f ##########"
        awk "${result}" $f
    done
    fi
}

Verwendungszweck:

echo "a b c" | m -p A 
echo "a b c" | m -i -p A # a b c

Sie können es in .bashrc einfügen, wenn Sie möchten.

0
ruanhao

Sie sollten grep so haben:

$ grep 'string1' file | grep 'string2'
0
Raghuram