AWK를 사용하여 ***로 구분된 단락을 추출합니다.

AWK를 사용하여 ***로 구분된 단락을 추출합니다.

다음과 같은 파일이 있습니다.

blablabla
blablabla
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

blablabla
blablabla

으로 단락을 추출하고 싶습니다 thingsIwantToRead. 이와 같은 문제를 처리해야 할 때 나는 다음을 사용했습니다.AWK이와 같이:

awk 'BEGIN{ FS="Separator above the paragraph"; RS="" } {print $2}' $file.txt | awk 'BEGIN{ FS="separator below the paragraph"; RS="" } {print $1}'

효과가 있었습니다.

이 경우에는 FS="***", "\*{3}", "\*\*" (AWK가 일반 별표로 처리하기 때문에 작동하지 않습니다) "\\*\\*"또는 제가 생각할 수 있는 정규식을 입력해 보았지만 작동하지 않습니다(아무것도 인쇄하지 않습니다).

이유를 아시나요?

그렇지 않다면 내 문제를 해결하는 다른 방법을 알고 있습니까?

다음은 구문 분석하려는 파일에서 발췌한 내용입니다.

13.2000000000     , 3*0.00000000000       ,  11.6500000000     , 3*0.00000000000       ,  17.8800000000

Blablabla

  SATELLITE EPHEMERIS
     ===================
Output frame: Mean of J2000

       Epoch                  A            E            I           RA           AofP          TA      Flight Ang
*****************************************************************************************************************
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
 <np>
 ----------------
 Predicted Orbit:
 ----------------

 Blablabla

나는 다음을 추출하고 싶다:

2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311

* 줄 뒤의 숫자를 얻으려고 시도한 명령은 다음과 같습니다.

`awk 'BEGIN{ FS="\\*{2,}"; RS="" } {print $2}' file | awk 'BEGIN{ FS="<np>"; RS="" } {print $1}'`

답변1

두 구분 기호 사이를 인쇄하도록 awk에 지시합니다. 구체적으로:

awk '/\*{4,}/,/<np>/' file

구분 기호가 포함된 줄도 인쇄되므로 다음을 사용하여 구분 기호를 제거할 수 있습니다.

awk '/\*{4,}/,/<np>/' file | tail -n +2 | head -n -1

또는 줄이 첫 번째 구분 기호와 일치하면 변수를 true로 설정하고 두 번째 구분 기호와 일치하면 false로 설정하고, true인 경우에만 인쇄할 수 있습니다.

awk '/\*{4,}/{a=1; next}/<np>/{a=0}(a==1){print}' file

a현재 줄이 4개 이상과 일치하면 위 명령은 이를 1로 설정 *하고 해당 next줄로 점프합니다. 이는 해당 ***행이 인쇄되지 않음을 의미합니다.


이것은 질문의 원래 오해된 버전에 대한 답변입니다. 약간 다른 상황에서 유용할 수 있기 때문에 여기에 남겨두겠습니다.

FS첫째, (필드 구분 기호)가 필요하지 않고 RS(레코드 구분 기호)가 필요합니다. 그런 다음 리터럴을 전달하려면 *이를 두 번 이스케이프 처리해야 합니다. 한 번은 백슬래시를 이스케이프 *하고 한 번은 백슬래시를 이스케이프합니다(그렇지 않으면 awk는 \ror 와 같은 방식으로 일치시키려고 시도합니다 \t). 그런 다음 두 번째 "줄"을 인쇄합니다.

$ awk -vRS='\\*\\*\\*' 'NR==2' file

thingsIwantToRead1   
thingsIwantToRead2   
thingsIwantToRead3  

출력 주위에 빈 줄을 피하려면 다음을 사용하십시오.

$ awk -vRS='\n\\*\\*\\*\n' 'NR==2' file
thingsIwantToRead1   
thingsIwantToRead2   
thingsIwantToRead3  

이는 ***이후에 가정합니다.제시하는 첫 번째 단락 바로 다음이 아닌 단락.

답변2

@terdon의 답변 외에도 awk (및 sed)를 사용하면 범위 모드를 사용할 수 있습니다.

awk '/sep1/,/sep2/{print}' file

또는

sed -n '/sep1/,/sep2/p' file

sep1다음 을 포함하여 모든 것을 인쇄합니다 sep2.

~$ awk '/sep1/,/sep2/{print}' file
sep1
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
sep2

귀하의 경우:

~$ awk '/\*\*\*/,/^$/{print}' file
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
 

그런 다음 첫 번째 행과 마지막 행을 삭제할 수 있습니다.

예를 들어:

~$ sed -n '/\*\*\*/,/^$/p' file | sed '1d;$d'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

또는

~$ awk '/\*\*\*/,/^$/{print}' file | awk 'NR>1&&!/^$/ {print}'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

단락이 너무 길지 않은 경우.

답변3

sed이 문제를 해결하는 방법에는 두 가지 가 있습니다 . 당신은 선택할 수 있습니다포함한또는오직. 귀하의 경우에는포함됨선택은 일치로 시작하는 모든 줄을 인쇄하는 것을 의미하며, '^*\*\*'그 중 최대 하나를 포함합니다.^ *<np> (그게 뭐든지)또는 ^$빈 줄.

하나포함됨다른 답변에 설명된 범위 표현식 중 하나를 사용하여 선택 항목을 지정할 수 있으며 다음을 지정하는 작업도 포함됩니다.여기서 인쇄 시작모드가 다음으로 전달됩니다.여기까지 다 지나갔어무늬.

하나독점적인다른 길로 가는 것을 선택하세요. 그것은다음 전에 인쇄를 중지하세요.모드가 다음으로 전달됩니다.여기에서 인쇄 시작무늬. 귀하의 예제 데이터에 대해 - 그리고 허용다음 전에 인쇄를 중지하세요.빈 줄과 일치하는 패턴또는<np>일:

sed -e 'x;/^\( *<np>.*\)*$/,/^*\** *$/c\' -e '' <infile >outfile
  • x
    • 홀드 공간과 패턴 공간을 교환합니다. 해당 기관은뒤를 봐sed-입력 후 항상 한 줄-그리고 첫 번째 줄은 항상 비어 있습니다.
  • /^\( *<np>.*\)*$/
    • 이는 다음을 선택합니다.다음 전에 인쇄를 중지하세요.처음부터 끝까지 0번 이상 일치하는 그룹의 행과 일치합니다. 0개 이상의 항목과 일치할 수 있는 두 가지 유형의 줄이 있습니다. 즉, 빈 줄 또는 빈 줄이 여러 개 있는 줄입니다.<스페이스>줄의 시작 부분에 문자열이 옵니다 <np>.
  • /^*\** *$/
    • 이는 다음을 선택합니다.여기에서 인쇄 시작하나 이상의 *별표 문자로 시작하고 줄 끝까지 계속되며 *별표가 0개 이상 발생하고 공백 수에 제한 없이 끝날 수 있는 줄입니다.
  • c\' -e ''
    • 이렇게 하면 c차단된 전체 선택 항목이 빈 줄에 걸려 불필요한 줄이 모두 문자열로 압축됩니다.EOF.

^*\** *$따라서 첫 번째 후속 단락 앞뒤에 나타나는 줄 수는 ^\( *<np>.*\)*$항상 단 하나의 공백으로 압축되고 해당 단락의 첫 번째 항목만 압축됩니다.뒤쪽에일치 항목은 ^*\** *$표준 출력으로 인쇄됩니다. 인쇄된다...

2012/10/01 00:00:00.000 6998.239 0.001233 97.95558 77.41733 89.98551 290.75808 359.93398
2012/10/01 00:05:00.000 6993.163 0.001168 97.95869 77.41920 124.72698 274.57362 359.93327
2012/10/01 00:10:00.000 6987.347 0.001004 97.96219 77.42327 170.94020 246.92395 359.94706
2012/10/01 00:15:00.000 6983.173 0.000893 97.96468 77.42930 224.76158 211.67042 359.97311

입력에 나타나는 단락 패턴을 원하는 만큼 처리하려고 한다고 가정해 보겠습니다. 당신이 원한다면첫 번째그러나 GNU grepinfile정규적이고,검색 가능문서:

{   grep -xm1 '*\** *'        >&2
    sed -n '/^\( *<np>.*\)*$/q;p'
}   <infile 2>/dev/null >outfile

...역시 작동할 겁니다.

실제로 제 생각에는방법. 세 번째는 다음과 같습니다.

sed 'H;$!d;x;s/\(\n\*\** *\n\(\([0-9./: ]*\n\)*\)\)*./\2/g'

...전체 파일을 읽고 일치하는 줄 사양에 속하지 않는 모든 문자를 전역적으로 바꿉니다. 이전과 마찬가지로 잘 인쇄되지만 쓰기가 힘들고 옵션 옵션의 균형을 맞출 경우에만 안전한 성능을 발휘합니다.어느특징.

답변4

질문 편집에 따라 업데이트된 버전:

펄 사용:

< inputfile perl -0777 -pe 's/.*[*]+\n(.*) <np>\n.*/$1/s' > outputfile
  • < inputfile: 콘텐츠를 's' inputfile로 리디렉션합니다.perlstdin
  • -0777: Perl이 파일을 한 줄씩 읽는 대신 한 번에 전체 파일을 읽도록 합니다.
  • -p: Perl이 이 줄을 인쇄하도록 강제합니다.
  • -e: Perl이 인수로부터 프로그램 라인을 읽도록 강제합니다.
  • > outputfileperl:컨텐츠를 stdout다음으로 리디렉션합니다.outputfile

정규식 분해:

  • s: 교체를 수행하도록 어설션
  • /: 검색 모드 시작
  • .*[*]+\n*: 개행 문자 바로 뒤에 하나 이상의 문자로 끝나는 문자열 끝까지 모든 문자와 일치합니다.
  • (.*) <np><np>\n: 뒤에 오는 문자열의 문자 까지 원하는 수의 모든 문자를 일치시키고 그룹화합니다.
  • .*: 임의 개수의 모든 문자와 일치합니다.
  • /: 검색 모드 중지/교체 모드 시작
  • $1: 캡처된 그룹으로 대체됨
  • /:교체 모드 중지/수정자 시작
  • s: 입력 문자열이 단일 줄로 처리되어 .개행 문자도 일치하도록 지정합니다.

예제 출력:

~/tmp$ cat inputfile
13.2000000000     , 3*0.00000000000       ,  11.6500000000     , 3*0.00000000000       ,  17.8800000000

Blablabla

  SATELLITE EPHEMERIS
     ===================
Output frame: Mean of J2000

       Epoch                  A            E            I           RA           AofP          TA      Flight Ang
*****************************************************************************************************************
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
 <np>
 ----------------
 Predicted Orbit:
 ----------------

 Blablabla
~/tmp$ < inputfile perl -0777 -pe 's/.*[*]+\n(.*) <np>\n.*/$1/s'
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
~/tmp$ 

원본:

펄 사용:

< inputfile perl -0777 -pe 's/.*[*]{3}\n(.*\n)\n.*/$1/s' > outputfile
  • < inputfile: 콘텐츠를 's' inputfile로 리디렉션합니다.perlstdin
  • -0777: Perl이 파일을 한 줄씩 읽는 대신 한 번에 전체 파일을 읽도록 합니다.
  • -p: Perl이 이 줄을 인쇄하도록 강제합니다.
  • -e: Perl이 인수로부터 프로그램 라인을 읽도록 강제합니다.
  • > outputfileperl:컨텐츠를 stdout다음으로 리디렉션합니다.outputfile

정규식 분해:

  • s: 교체를 수행하도록 어설션
  • /: 검색 모드 시작
  • .*[*]{3}\n***\n: 문자열이 끝나기 전의 모든 문자와 일치합니다.
  • (.*\n)\n: 개행 문자까지 포함하고 그 뒤에 개행 문자가 오는 문자를 원하는 수만큼 일치시키고 그룹화합니다.
  • .*: 임의 개수의 모든 문자와 일치합니다.
  • /: 검색 모드 중지/교체 모드 시작
  • $1: 캡처된 그룹으로 대체됨
  • /:교체 모드 중지/수정자 시작
  • s: 입력 문자열이 단일 줄로 처리되어 .개행 문자도 일치하도록 지정합니다.

예제 출력:

~/tmp$ cat inputfile
blablabla
blablabla
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

blablabla
blablabla
~/tmp$ < inputfile perl -0777 -pe 's/.*[*]{3}\n(.*\n)\n.*/$1/s'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
~/tmp$ 

관련 정보