Wie greife ich nach Inhalten nach Mustern?


79

Bei einer gegebenen Datei zum Beispiel:

potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789

Ich möchte nach allen Zeilen suchen, die mit beginnen, potato:aber nur die folgenden Zahlen weiterleiten potato:. Im obigen Beispiel wäre die Ausgabe also:

1234
5432

Wie kann ich das machen?

Antworten:


112
grep 'potato:' file.txt | sed 's/^.*: //'

grepsucht nach einer Zeile, die die Zeichenfolge enthält potato:, und sedersetzt ( s///- ersetzt) für jede dieser Zeilen jedes Zeichen ( .*) vom Zeilenanfang ( ^) bis zum letzten Auftreten der Sequenz :(Doppelpunkt gefolgt von Leerzeichen) durch das Leerzeichen Zeichenfolge ( s/...//- Ersetzen Sie den ersten Teil durch den zweiten Teil, der leer ist).

oder

grep 'potato:' file.txt | cut -d\   -f2

Teilen Sie für jede Zeile, die enthält potato:, cutdie Zeile in mehrere durch Leerzeichen begrenzte Felder auf ( -d\- d= Trennzeichen, \= Leerzeichen, so etwas -d" "hätte auch funktioniert) und drucken Sie das zweite Feld jeder solchen Zeile aus ( -f2).

oder

grep 'potato:' file.txt | awk '{print $2}'

Gibt für jede Zeile, die enthält potato:, awkdas zweite Feld ( print $2) aus, das standardmäßig durch Leerzeichen getrennt ist.

oder

grep 'potato:' file.txt | perl -e 'for(<>){s/^.*: //;print}'

Alle darin enthaltenen Zeilen potato:werden an ein inline ( -e) - Perl- Skript gesendet, aus dem alle Zeilen stammen stdin. Anschließend wird für jede dieser Zeilen dieselbe Ersetzung wie im ersten Beispiel oben durchgeführt und anschließend gedruckt.

oder

awk '{if(/potato:/) print $2}' < file.txt

Die Datei wird über stdin( < file.txtsendet den Inhalt der Datei über stdinden Befehl auf der linken Seite) an ein awkSkript gesendet , das für jede Zeile, die enthält potato:( if(/potato:/)gibt true zurück, wenn der reguläre Ausdruck /potato:/mit der aktuellen Zeile übereinstimmt), das zweite Feld wie beschrieben druckt über.

oder

perl -e 'for(<>){/potato:/ && s/^.*: // && print}' < file.txt

Die Datei wird über stdin( < file.txtsiehe oben) an ein Perl-Skript gesendet , das ähnlich wie oben funktioniert. Diesmal wird jedoch auch sichergestellt, dass jede Zeile die Zeichenfolge enthält potato:( /potato:/ein regulärer Ausdruck, der übereinstimmt, wenn die aktuelle Zeile enthält potato:, und wenn es tut ( &&), wendet dann den oben beschriebenen regulären Ausdruck an und druckt das Ergebnis).


3
Keine zwei Prozesse und ein Rohr erforderlich. Ich würde gehen für awk '$1 ~ /potato/ { print $2 }' file.txt.
Musiphil

2
Der Awk wäre idiomatischerawk '/potato:/ {print $2}'
Benjamin W.

Die Perl-Skripte könnten davon profitierenperl -pe
Tripleee

59

Oder verwenden Sie Regex-Behauptungen: grep -oP '(?<=potato: ).*' file.txt


4
Ich habe einige Einzeiler aus der oben akzeptierten Antwort ausprobiert, aber ich habe das Gefühl, dass diese Antwort die Frage genauer löst.
Jake88

3
Einige Erklärungen: Option -obedeutet, dass nur der passende Teil der Zeile gedruckt wird. Während -Pein Perl-kompatibler regulärer Ausdruck folgt , der zufällig ein positiver Look hinter Regex ist (?<=string).
Serge Stroobandt

9
sed -n 's/^potato:[[:space:]]*//p' file.txt

Man kann sich Grep als eingeschränkten Sed oder Sed als generalisierten Grep vorstellen. In diesem Fall ist Sed ein gutes, leichtes Werkzeug, das macht, was Sie wollen - obwohl es natürlich auch mehrere andere vernünftige Möglichkeiten gibt, dies zu tun.


2

Dies druckt alles nach jedem Spiel nur in derselben Zeile:

perl -lne 'print $1 if /^potato:\s*(.*)/' file.txt

Dies wird dasselbe tun, außer dass auch alle nachfolgenden Zeilen gedruckt werden:

perl -lne 'if ($found){print} elsif (/^potato:\s*(.*)/){print $1; $found++}' file.txt

Diese Befehlszeilenoptionen werden verwendet:

  • -n Schleife um jede Zeile der Eingabedatei
  • -l Entfernt Zeilenumbrüche vor der Verarbeitung und fügt sie anschließend wieder hinzu
  • -e Führen Sie den Perl-Code aus

2
grep -Po 'potato:\s\K.*' file

-P Perl regulären Ausdruck verwenden

-o um nur die Übereinstimmung auszugeben

\s um den Raum danach anzupassen potato:

\K das Match weglassen

.* um mit dem Rest der Zeichenfolge (n) übereinzustimmen


1

Sie können grep verwenden, wie in den anderen Antworten angegeben. Sie benötigen jedoch kein grep, awk, sed, perl, cut oder externes Werkzeug. Sie können es mit purer Bash tun.

Versuchen Sie dies (Semikolons sind vorhanden, damit Sie alles in eine Zeile setzen können):

$ while read line;
  do
    if [[ "${line%%:\ *}" == "potato" ]];
    then
      echo ${line##*:\ };
    fi;
  done< file.txt

## weist bash an, die längste Übereinstimmung von ":" in $ line von vorne zu löschen.

$ while read line; do echo ${line##*:\ }; done< file.txt
1234
5678
5432
4567
5432
56789

oder wenn Sie den Schlüssel anstelle des Werts möchten, weist %% bash an, die längste Übereinstimmung von ":" in der $ -Zeile am Ende zu löschen.

$ while read line; do echo ${line%%:\ *}; done< file.txt
potato
apple
potato
grape
banana
sushi

Der Teilstring, auf den aufgeteilt werden soll, lautet ": \", da das Leerzeichen mit dem Backslash maskiert werden muss.

Weitere Informationen finden Sie im Linux-Dokumentationsprojekt .


while readist extrem langsam; Die Verwendung eines externen Dienstprogramms ist tatsächlich viel schneller, solange Sie eines mit gepufferten E / A auswählen (dh praktisch eines der in dieser Antwort genannten und viele andere).
Tripleee

Sie sollten read -res auch verwenden, es sei denn, Sie benötigen speziell ein ziemlich lästiges Legacy-Verhalten vor POSIX.
Tripleee

0

Modern BASH unterstützt reguläre Ausdrücke:

while read -r line; do
  if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
    echo "${BASH_REMATCH[1]}"
  fi
done
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.