여러 줄의 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

두 번째 일치 항목은 첫 번째 일치 항목 이후에 정확하게 인쇄되는 것을 볼 수 있습니다(줄 바꿈이나 구분 기호 없음). 이러한 이벤트를 파일로 분리할 수 없습니다.

원하는 출력은 다음과 같습니다.

파일 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그러나 이를 사용하는 대신 -P( Perl의 비표준 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

또는 전체 파일을 전체적으로 가져와 정규식을 사용하는 대신 한 줄씩 읽으십시오.

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부터 시작하는 두 자리 숫자가 옵니다.
  • --suppress-matched- 패턴( )과 일치하는 줄을 억제합니다 /^ \{4\}-1$/.
    • 이는 분할이 패턴별로 수행되기 때문에 필요합니다 csplit(첫 번째 줄과 마지막 줄을 알 수 없으며 패턴은 하나만 있습니다). 따라서 각 "꺼진" 패턴 후에는 해당 패턴만 포함하는 파일이 생성됩니다. 아래에서 다시 분할됩니다). 모드를 억제하는 경우 다음 플래그를 사용하여 이를 방지할 수 있습니다.
  • -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이 있는 행이 억제되고 결과 example01example03비어 있으므로 생성되지 않습니다.

답변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 

예제에서 위 명령을 실행하면 다음 내용이 포함된 두 개의 파일이 생성됩니다.

$ 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

관련 정보