grepまたはawkを使用して、パターン間のテキストブロック/行を印刷します。

grepまたはawkを使用して、パターン間のテキストブロック/行を印刷します。

私のファイルはfile.txt次のとおりです。

[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

grep次の3つの異なる出力を達成するために、これをbashスクリプトで使用または使用したいと思います。awk

  1. 出力

    [NamesB]   
    Bernd   
    Bruno
    
  2. 出力

    [NamesB]
    Bernd
    Bruno
    
    [NamesC] 
    Casper
    
  3. 出力

    [NamesD]
    Doris
    

私は試した:

grep  -oP '\[NamesB\].*?' file.txt

ただし、[NamesB]代わりに次のテキストブロックのみを取得します。私はテキストをすぐ後ろにインポートしましたが、新しい行にはインポートできませんでした。

それはすべてです。で始まる次の行を少なくともすべて得ることができれば[NamesB]それはうまくいきません。

  • したがって、1.の出力は、[NamesB]次の項目で始まり終わるすべての項目を印刷することで最も簡単になると想像できます[
  • 2番は出力1番と似ていますが、2回実行されることを想像するgrepこともできます。一度[NamesB]、その後[NamesC]

[しかし、以下がないので、3.ではこれはどのように機能しますか?そして、で始まる未知の次のブロックがあるかもしれません[

その後、コマンドはまたは[NamesB]で始まるテキストの印刷を開始し、次に開く角かっこまたは[ファイルの終わりで停止する必要があります。

PS:同様の質問を投稿して解決策を見つけましたが、全文行。この質問では、別の状況、つまり1行ではなくテキストブロックがあります。

答え1

印刷するレコードを選択するための要件は明確ではありませんが、おそらくこれがawkでやりたいことです。

出力1、オプション1(一度に1行ずつ読みます):

$ awk '/^\[/{f=(/^\[NamesB]/)} f' file.txt
[NamesB]
Bernd
Bruno

出力1、オプション2(一度に1つの複数行レコードを読む)

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"' file.txt
[NamesB]
Bernd
Bruno

出力2、オプション1(NamesBレコードとそれに続くレコードを印刷):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"{c=2} c&&c--' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

出力2、オプション2(入力位置に関係なくNamesBおよびNamesCレコードを印刷):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 ~ /^\[Names[BC]]$/' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

出力3、オプション1(NamesDレコード印刷):

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesD]"' file.txt
[NamesD]
Doris

出力3、オプション2(名前が何であれ、入力の4番目のレコードを印刷します):

$ awk -v RS= -v ORS='\n\n' -F'\n' 'NR == 4' file.txt
[NamesD]
Doris

また、以下に関連して:

少なくとも[NamesB]で始まる次の行をすべて取得できる場合

以下はトリックを行います。

$ awk -v RS= -v ORS='\n\n' -F'\n' '$1 == "[NamesB]"{f=1} f' file.txt
[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

もちろん、さまざまな基準に基づいて出力を生成するために他の多くのスクリプトを書くことができます。正しいスクリプトは、出力するチャンクを選択するときの要件によって異なります。

答え2

Raku(以前のPerl_6)の使用

~$ raku -e 'for slurp.split("\n\n") { .put if / \[ NamesA \]  /};'   file

#OR

~$ raku -e '.put if / \[ NamesA \]  / for slurp.split("\n\n");'   file

上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。つまり、ファイルは2つの連続した改行文字をslurp使用して一度にメモリに書き込まれます。結果要素(レコード)は次のように繰り返されます。必須正規表現に一致する項目が見つかり、要素(レコード)が出力されます。split\n\nforifput

入力例:

[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

[NamesC]
Casper

[NamesD]
Doris

出力例:

[NamesA]
Andreas
Alex

|正規表現マッチングでOR記号を使用すると、複数のレコードを返すことができます。戻り値を適切に分離するには、$_.putまたは部分を書き換えて各レコードに末尾の改行文字を埋めることができます。.putput "$_\n"

~$ raku -e 'put "$_\n" if / \[ NamesA | NamesB \]  / for slurp.split("\n\n");'   file
[NamesA]
Andreas
Alex

[NamesB]
Bernd
Bruno

注:正規表現マッチングはレコード内の任意の行にすることができます。最初の行を具体的に一致させるには、/^ \[ NamesA \] $$ /ここでは^文字列の始まりを意味し、行の$$終わりを意味します。

https://docs.raku.org
https://raku.org

答え3

以下を使用して、すべてのブロック(NamesAなど)を抽出できます。

$ awk '/^\[NamesA/{p=1; print; next} /^\[/{p=0}; p>0{print}' input_file
[NamesA]
Andreas
Alex

コードに示すように、ブロックヘッダーの最初の文字である[をエスケープする必要があります。

この1行を使用すると、必要に応じて出力の組み合わせを印刷できます。

関連情報