複数行の grep は、別のファイルが表示されるたびに検索します。

複数行の grep は、別のファイルが表示されるたびに検索します。

次のファイルがあります。

はい.txt

    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1
 
#ffafsda
    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1
    
    asfasd
    
    afsdasdf

常に正確に一致する行で始まり終わるブロックで構成されています^ {4}-1$。このチャンクでファイルを複数に分割する必要があります。

私が今考えているのは、これらの塊を抽出する複数行の正規表現です。

grep -Pzo '(?s)((?m:^)\s{4}-1(?m:$).*?(?m:^)\s{4}-1(?m:$))' example.txt

出力:

    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1

2番目の一致は、最初の一致の後に正確に印刷されることがあります(改行や区切り文字なし)。これらのイベントをファイルに分割することはできません。

希望の出力は次のとおりです。

ファイル1:

    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1

ファイル2

    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1

助けてくれてありがとう。

答え1

-z(非標準 GNU 拡張)、NUL で区切られたレコードで動作しますが、そうではgrepありません。複数行 grep、だから:

  • 一致は、NULで区切られた各レコードに対して独立して実行されるか、区切りがない場合は完全な入力で実行されます(未区分のレコードを使用する機能は別のGNU拡張です)。
  • -o別の非標準GNU拡張)各一致に対してNULで区切られた出力

したがって、出力の記録ははい別途(実際には分離)。sed -n lたとえば、出力を渡すと、次のようになります。

$ grep -Pzo '(?s)((?m:^)\s{4}-1(?m:$).*?(?m:^)\s{4}-1(?m:$))' example.txt | sed -n l
    -1$
    15$
         1         0         0        11 -1.0000E+001  1.0000E+001 -1\
.0000E+001$
         2         0         0        11  1.0000E+001  1.0000E+001 -1\
.0000E+001$
...$
        29         0         0        11  1.0000E+001  2.0000E+001  1\
.0000E+001$
        30         0         0        11  5.0000E+000  5.0000E+000  5\
.0000E+000$
    -1\000    -1$
    780$
         1       116         1         2         1         1         \
7        20$
         1        11         2        15         4        18         \
3        12$
        13        16        22        19         5        24         \
9        29$
         8        27         6        23$
    -1\000$

\000各一致を区別するsを確認してください。

ここで一致を単純化できます。

grep -Pzo '(?sm)(^\s{4}-1$).*?(?1)' example.txt

grepただし、これを使用する代わりに-PPerlの非標準GNU拡張でもある)、実際に使用することができます。これにはいくつかの利点があります。

  • PerlはGNU grepよりも多くのシステムに存在するため、移植性が高くなります(Perlと同様の正規表現サポートがGNUビルドで常に有効になるわけではありませんgrep)。
  • Perlは-0NULで区切られたレコードを使用する必要がありますが、これはあなたが望むものではありません。あなたはフルルックを飲むモードが欲しいですperl-0777
  • Perlは独自に別々のファイルに出力を書き込むことができます。
perl -l -0777 -ne '
  while (/(^\s{4}-1$).*?(?1)/msg) {
    open OUT, ">", "output-" . ++$n . ".txt" or die;
    print OUT $&
  }' example.txt

または、ファイル全体を完全にインポートし、正規表現を使用する代わりに1行ずつ読みます。

perl -ne '
  if (/^\s{4}-1$/) {
    if ($inside = 1 - $inside) {
      open OUT, ">", "output-" . ++$n . ".txt" or die;
    } else {
      print OUT; next
    }
  }
  print OUT if $inside' example.txt

(すべて一致しなくても別の結果が表示されます-1)。


1これについては、GNUがオプションとして使用する(利用可能な)PCRE2に付属のサンプルアプリケーションpcre2grep -M(以前のpcregrep -M)を参照してください。pcre2grepgrep-P

答え2

代わりに、全体の塊を得る別の方法grep

まず、次のようにsed生成することをお勧めします。

sed -n '/^ \{4\}-1$/,/^ \{4\}-1$/p' example.txt
    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1
    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1

チャンクを別のファイルに分割

その後利用できますcsplitコマンドはパターンに従ってファイルを分割します。

名前

csplit- ファイルをコンテキストラインによって決定された部分に分割する

要約

csplit[オプション]...ファイルモード...

説明する

PATTERNで区切られたFILEフラグメントを「xx00」、「xx01」、...ファイルに出力し、各フラグメントのバイト数を標準出力に出力します。

はい

$ sed -n '/^ \{4\}-1$/,/^ \{4\}-1$/p' example.txt | csplit - -f example --suppress-matched -z '/^ \{4\}-1$/' '{*}'
331
292

説明する:

  • csplit -- 標準入力から読み出します。
  • -f example- ファイルのプレフィックスを「example」に設定します(デフォルトは「xx」の代わりに)。各プレフィックスの後には、00から始まる2桁の数字が続きます。
  • --suppress-matched- パターン()に一致する行を抑制します/^ \{4\}-1$/
    • これは、分割がパターンごとに行われるために必要ですcsplit(最初の行と最後の行は不明で、パターンは1つだけです)。したがって、各「オフ」パターンの後、そのパターンのみを含むファイルが生成されます。下から再分割されます)。モードを抑制する場合は、次のフラグを使用してこれを回避できます。
  • -z- 空の出力ファイルを削除
  • '/^ \{4\}-1$/'- パターンはファイルを分割する場所を示します。
  • '{*}'- 前のパターンをできるだけ繰り返します。

生成されるすべてのファイルのサイズを出力します。

結果:必要なブロックがあるがパターンがない2つのファイル。

$ cat example00
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000

$ cat example01
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23

ファイルに区切られた行(最初の行と最後の行)を返すには、-1次のコマンドを使用します。

sed -i '1s/.*/    -1\n\0/; $s/$/\n    -1/' example[0-9][0-9]

--suppress-matched-z表示の詳細な説明

説明のために--suppress-matched何が起こるかをお見せしましょう。

$ sed -n '/^ \{4\}-1$/,/^ \{4\}-1$/p' example.txt | csplit -f example  -z - '/^ \{4\}-1$/' '{*}'
338
7
299
7

4つのファイルが作成されます。パターンexample01のみが含まれていますexample03

$ cat example00
    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000

$ cat example01
    -1

$ cat example02
    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23

$ cat example03
    -1

使用すると、--suppress-matched-1のある行が抑制され、結果は空であるためexample01生成example03されません。

答え3

正規表現を「行」を定義するレコード区切り文字として使用できるようにするGNU awkを使用できます。ここでは に設定できます\n -1\n。これは改行文字1個、スペース4個-1、改行文字1個です。その後、目的のセクションの始めと終わりに表示されるため、本質的に他のすべての「行」が必要になるため、行番号モジュールで2が0のときに印刷できます。

gawk '
  BEGIN{
    RS="\n    -1\n"; 
    ORS=RS
  } 
  NR % 2 ==0 { print RS $0 > "outfile." ++c }' file 

例では、上記のコマンドを実行すると、次の内容を含む2つのファイルが生成されます。

$ ls
file  outfile.1  outfile.2
$ cat outfile.1

    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1
$ cat outfile.2

    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1

これは、各ファイルの先頭に空行を追加する不幸な副作用があります。これが問題の場合は、-1明示的な内容を印刷するだけです。

gawk '
  BEGIN{
    RS="\n    -1\n"; 
  } 
  NR % 2 ==0 { printf "   -1\n%s\n    -1\n", $0 > "outfile." ++c }' file 

答え4

awkを使用してください。

$ cat tst.awk
/^    -1/ {
    if ( inBlock ) {
        print > out; close(out)
    }
    else {
        out = FILENAME "_" (++cnt)
    }
    inBlock = !inBlock
}
inBlock { print > out }

$ awk -f tst.awk example.txt

$ head example.txt_*
==> example.txt_1 <==
    -1
    15
         1         0         0        11 -1.0000E+001  1.0000E+001 -1.0000E+001
         2         0         0        11  1.0000E+001  1.0000E+001 -1.0000E+001
...
        29         0         0        11  1.0000E+001  2.0000E+001  1.0000E+001
        30         0         0        11  5.0000E+000  5.0000E+000  5.0000E+000
    -1

==> example.txt_2 <==
    -1
    780
         1       116         1         2         1         1         7        20
         1        11         2        15         4        18         3        12
        13        16        22        19         5        24         9        29
         8        27         6        23
    -1

関連情報