テキストファイルから、ある文字列の最初の出現から別の文字列の最初の出現までの部分を抽出します。

テキストファイルから、ある文字列の最初の出現から別の文字列の最初の出現までの部分を抽出します。

大きなテキスト ファイルから、FOO の最初の出現から BAR の最初の出現までの部分を抽出するにはどうすればよいでしょうか?

私の場合は、mysqldump によって作成された sql ファイルの一部を抽出しようとしています。

答え1

クレジット翻訳者そして@パウロフィードバックで助けてくれた人たち!最後の perlここにワンライナーがあります:

perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' file

説明:

if(/FOO/../BAR/){        # perform the following actions on each line, starting
                         # with a line that contains FOO, and up to and including
                         # a line that contains BAR  
s/.*?(FOO)/$1/ if!$i++;  # only on the first line that contains FOO,
                         # delete all characters before FOO  
s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters
                         # after BAR, print the line and stop processing  
print                    # simply print the line contents

古い回答:

クレジット@パウロ簡単なsed解決策です。 次のように読むのも簡単ですawk

awk '/FOO/,/BAR/' file

ただし、これは単純すぎるかもしれません。行全体を返すのであって、「FOO の最初の出現から始まり、BAR の最初の出現で終わるテキストの一部」を返すのではありません。これは、FOO が最初の単語で、BAR が最後の単語であるべきという意味だと思います。これを正確に実行するには、より複雑な答えが必要です。 でそれを実現してみましょうperl

単純なケース(行全体を返す):

perl -lne 'print if /FOO/../BAR/' file

複雑なケース(FOO から BAR まで):

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file

範囲演算子に変数を割り当てる、この同等のソリューションが気に入っています。

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file

注記:抽出するテキスト部分は 1 つだけであると想定されます。つまり、FOO と BAR で区切られた最初の段落の後に別の FOO が出現することはありません。

そうでなければ、単純なケースはすでにそれほど単純ではなくなりますawk

awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file

そしてperl

perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file

そして、より複雑で洗練されたソリューションは次のようになります。

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file

そして:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file

この例は、問題に少しだけ複雑さを加えただけで、非常に明確で説明の必要がないワンライナーが、意味の分からないランダムな文字列のように見えるようになる様子を示しています。必要に応じて、追加機能を簡単に追加でき、コーナーケースを考慮できる、スタンドアロンで保守可能で読みやすいスクリプトを作成することをお勧めします。

答え2

この場合は、思ったほど難しくありませんでした。 ではsed、FOO の最初の出現から BAR の最初の出現までです (試していませんが、おそらく 2 番目の FOO から 2 番目の BAR のようなものはより難しいでしょう)。

sed -nr '/FOO/ {
/FOO/ s/[^F]+FOO/FOO/p
:a
n
/BAR/ s/([^B]+BAR).*/\1/
p
/BAR/ q
ba
}' <<<'line1
> line2 FOO text1 FOO text2
> line3
> line4 BAR text3 BAR text4
> line5'

FOO text1 FOO text2
line3
line4 BAR

関連情報