あるパターンの最後の出現を別のパターンの前に取得する

あるパターンの最後の出現を別のパターンの前に取得する

次のようなファイルの場合:

...
Pattern2:TheWrongBar
foo 
Pattern2:TheRightBar
foo 
First Pattern
foo
...

この場合は、Pattern2その前の最後の出現箇所を見つける必要があります。First PatternPattern2:TheRightBar

私の最初のアイデアは、次のようにして残りのファイルをすべて取得することですFirst pattern

sed -e '/First Pattern/,$d' myfile | tac | grep -m1 "Pattern I need to get"

このコードを最適化する方法はないのでしょうか?

答え1

awk

awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
  • /Pattern2/ {line=$0; next}: パターンPattern2が一致した場合、その行を変数に保存しline、次の行に進みます

  • /First Pattern/ {print line; exit}:First Patternが見つかった場合は変数を出力しline、終了します

例:

% 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

答え2

走れる

sed '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/!d;q' infile

使い方:

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)

これは、少なくとも1行が一致する行が、PATTERN2ある行の一致の前にある場合にのみ終了しますPATTERN1。そのため、次のような入力では、

1
2
PATTERN1
PATTERN2--1st
3
PATTERN2--2nd
PATTERN1
...

印刷されます

PATTERN2--2nd

代わりに、最初のマッチで終了したい場合はPATTERN1、次のように実行します。

sed -n '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/p;q' infile

上記の入力では何も印刷されません (これは、まさにあなたのソリューションと同じことを行います)。

答え3

「最初のパターン」の行数を検索し、head を使用してその上の行を表示し、tac にパイプして grep します。

head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2" 

例えば。

head --lines=+6 file | tac | grep -m1 "Pattern2" 

これは、grep で -m 1000000 を使用するよりも信頼性が高いです。速度は OP にとって重要なので、実行時間をチェックしましたが、他のすべての現在の回答よりも高速であるように見えます (私のシステム上)

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

答え4

最も効率的な方法であることが判明私の場合だった:

grep -m1 "First Pattern" my_file -B 10000000 | tac | grep -m1 "Pattern2"

明らかに、-Bオプションは一部の例では使用できませんが、またはgrepよりもはるかに高速なので、そのソリューションを選択しました。オプションの値が高くなると、検索の効率が大幅に低下します。awksed-B

関連情報