다음과 같은 파일이 있습니다.
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는 \r
or 와 같은 방식으로 일치시키려고 시도합니다 \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번 이상 일치하는 그룹의 행과 일치합니다. 0개 이상의 항목과 일치할 수 있는 두 가지 유형의 줄이 있습니다. 즉, 빈 줄 또는 빈 줄이 여러 개 있는 줄입니다.<스페이스>줄의 시작 부분에 문자열이 옵니다
/^*\** *$/
- 이는 다음을 선택합니다.여기에서 인쇄 시작하나 이상의
*
별표 문자로 시작하고 줄 끝까지 계속되며*
별표가 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 grep
와infile
정규적이고,검색 가능문서:
{ 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
로 리디렉션합니다.perl
stdin
-0777
: Perl이 파일을 한 줄씩 읽는 대신 한 번에 전체 파일을 읽도록 합니다.-p
: Perl이 이 줄을 인쇄하도록 강제합니다.-e
: Perl이 인수로부터 프로그램 라인을 읽도록 강제합니다.> outputfile
perl
:컨텐츠를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
로 리디렉션합니다.perl
stdin
-0777
: Perl이 파일을 한 줄씩 읽는 대신 한 번에 전체 파일을 읽도록 합니다.-p
: Perl이 이 줄을 인쇄하도록 강제합니다.-e
: Perl이 인수로부터 프로그램 라인을 읽도록 강제합니다.> outputfile
perl
:컨텐츠를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$