In einer Datei wie dieser:
...
Pattern2:TheWrongBar
foo
Pattern2:TheRightBar
foo
First Pattern
foo
...
Ich muss das letzte Vorkommen davon finden, Pattern2
das vor First Pattern
dem in diesem Fall wärePattern2:TheRightBar
Meine erste Idee besteht darin, alle verbleibenden Dateien vorher abzurufen First pattern
mit:
sed -e '/First Pattern/,$d' myfile | tac | grep -m1 "Pattern I need to get"
Gibt es keine Möglichkeit, diesen Code zu optimieren?
Antwort1
Mit awk
:
awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
/Pattern2/ {line=$0; next}
: Wenn das MusterPattern2
übereinstimmt, wird die Zeile in der Variablen gespeichertline
und zur nächsten Zeile gewechselt/First Pattern/ {print line; exit}
: wennFirst Pattern
gefunden, die Variable druckenline
und beenden
Beispiel:
% cat file.txt
...
Pattern2:TheWrongBar
foo
Pattern2:TheRightBar
foo
First Pattern
foo
...
% awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
Pattern2:TheRightBar
Antwort2
Du könntest rennen
sed '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/!d;q' infile
Wie es funktioniert:
sed '/PATTERN2/h # if line matches PATTERN2 save it to hold buffer
/PATTERN1/!d # if it doesn't match PATTERN1 delete it
x # exchange buffers
/PATTERN2/!d # if current pattern space doesn't match delete it
q' infile # quit (auto-printing the current pattern space)
Dies würde nur beendet, wenn es mindestens eine Zeilenübereinstimmung PATTERN2
vor einer Zeilenübereinstimmung gibt PATTERN1
, also mit einer Eingabe wie
1
2
PATTERN1
PATTERN2--1st
3
PATTERN2--2nd
PATTERN1
...
es wird gedruckt
PATTERN2--2nd
Wenn Sie stattdessen beim ersten Treffer oder unabhängig davon beenden möchten PATTERN1
, führen Sie aus
sed -n '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/p;q' infile
das mit der obigen Eingabe nichts ausgibt (dieses macht genau das, was Ihre Lösung macht).
Antwort3
Findet die Zeilenanzahl des „ersten Musters“, verwendet dann head, um die darüber liegenden Zeilen anzuzeigen, leitet durch tac und grepst es.
head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2"
Z.B.
head --lines=+6 file | tac | grep -m1 "Pattern2"
Dies ist zuverlässiger als die Verwendung von -m 1000000 in grep. Da Geschwindigkeit für OP wichtig ist, habe ich die Laufzeit überprüft und sie scheint auch schneller zu sein als alle anderen aktuellen Antworten (auf meinem System).
wc -l file
25910209 file
time awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file
Pattern2:TheRightBar
real 0m2.881s
user 0m2.844s
sys 0m0.036s
time sed '/Pattern2/h;/First Pattern/!d;x;/Pattern2/!d;q' file
Pattern2:TheRightBar
real 0m5.218s
user 0m5.192s
sys 0m0.024s
time (grep -m1 "First Pattern" file -B 10000000 | tac | grep -m1 "Pattern2")
real 0m0.624s
user 0m0.552s
sys 0m0.124s
time (head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2")
Pattern2:TheRightBar
real 0m0.586s
user 0m0.528s
sys 0m0.160s
Antwort4
Der effizienteste Weg istin meinem FallWar:
grep -m1 "First Pattern" my_file -B 10000000 | tac | grep -m1 "Pattern2"
Offensichtlich kann die -B
Option in einigen Beispielen nicht verwendet werden, grep
ist aber so viel schneller als awk
oder sed
, dass ich mich für diese Lösung entschieden habe. Wenn der Wert für die Option -B
höher wird, ist die Suche viel weniger effizient.