sed 정규식은 패턴을 포함하는 전체 단락을 캡처할 수 없습니다.

sed 정규식은 패턴을 포함하는 전체 단락을 캡처할 수 없습니다.

이 XML 파일이 있습니다(예제).

<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>


<This is a line of text with a year=33020 month=12 in it
This line of text does not have a year or month in it
This year=33020 is the current year the current month=1
This is the year=33020 the month=2/>

내 Linux 배포판(sed(GNU sed) 4.2.2)과 함께 제공된 설치를 사용하여 sed다음 정규식을 사용하여 이 파일을 검색합니다.

 sed -En 'N;s/\<(This.*2020.*[\s\S\n]*?)\>/\1/gp' test2.txt

그러나 다음 문자열만 캡처합니다.

<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it

하지만 먼저 전체를 포착하려고 노력합니다.패턴 사이 <및 포함.>

내가 여기서 뭘 잘못하고 있는 걸까?

답변1

이것이 예상대로 작동하지 않는 이유는 <정규식 >에서 이스케이프 처리할 필요가 없고 특별한 의미가 없기 때문입니다. 그러나, \<그리고\> 하다GNU 확장 정규식( 활성화 포함 -E)의 경우 특별한 의미가 있습니다. 즉, 단어 경계에서 일치합니다. 단어의 \<시작과 끝을 일치시킵니다 . \>따라서 \<(This실제로는 와 일치하지 않지만 <단어의 시작 부분과 일치합니다 This. \>마지막 것도 마찬가지다. GNU sed매뉴얼에는한 가지 예이것은 거의 정확히 당신이 추구하는 것입니다:

$ sed -En '/./{H;1h;$!d} ; x; s/(<This.*2020.*?>)/\1/p;' file
<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>

sed이런 종류의 작업에는 특히 적합하지 않다고 생각합니다 . 나는 다음을 사용할 것이다 perl:

$ perl -000 -ne 'chomp;/<.*2020.*?>/s && print "$_\n"; exit' file
<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>

여기서는 "단락 모드"()에서 Perl을 사용하고 있습니다. -000이는 "줄"이 두 개의 연속 \n문자(즉, 빈 줄)로 정의된다는 의미입니다. 이 스크립트는 다음을 수행합니다.

  • chomp: "줄"(문단) 끝에 있는 후행 줄 바꿈을 제거합니다.
  • /<.*2020.*?>/s && print "$_\n": 이 "줄"(단락)이 <0개 이상의 문자, 20200개 이상의 문자, 0개 이상의 문자와 일치하는 경우 >줄 바꿈 문자( print "$_\n")가 추가되어 인쇄됩니다. s일치 연산자에 대한 수정자는 .개행 문자 일치를 허용합니다.

또 다른 옵션은 다음과 같습니다 awk.

$ awk 'BEGIN{RS="\n\n"} /<.*2020.+?>/' file
<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>

레코드 구분 기호를 RS두 개의 연속된 줄 바꿈으로 설정한 다음 위와 동일한 정규식을 사용하여 일치시킵니다. 일치 항목이 발견될 때(또는 다른 작업이 true를 반환할 때) 기본 동작은 현재 레코드를 인쇄하는 것이므로 awk필요한 내용이 인쇄됩니다.

답변2

첫째, 대부분의 텍스트 처리 도구(예: sed또는 ) awk는 한 줄씩 작업하므로 전체 단락을 일치시키려면 약간의 추가 노력이 필요합니다. 이는 가능하지만 예상치 못한 출력이 나타나는 이유 중 하나이기도 합니다.

둘째, XML 태그 구분 기호로 인해 입력이 구조화된 텍스트처럼 보입니다. 따라서 xmlstarlet이를 처리하려면 기타 전문 도구를 사용하는 것이 가장 좋습니다 . (고쳐 쓰다: 이제 귀하의 의견에서 이를 확인하셨으므로 xmlstarlet또는 유사한 도구를 사용하는 것이 좋습니다. )

즉, 텍스트가 예제와 유사하고 설치가 awk다중 문자 레코드 구분 기호(예: GNU Awk)를 허용하는 경우 다음 프로그램이 작동해야 합니다.

awk -v RS="<|/>" '/2020/' input.txt

변수 RS에 두 개 이상의 문자가 포함되어 있으면 정규식으로 해석되므로 a <또는 a가 />기본값 대신 "레코드 구분 기호"로 간주됩니다 \n. 따라서 일치 조건은 개별 줄뿐만 아니라 이러한 태그 사이의 전체 텍스트에 적용됩니다.

결과:

This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2

"태그 열기" <및 "태그 닫기" />문자 조합은 레코드 구분 기호로 선택되었기 때문에 출력에서 ​​제거됩니다. 반면에 이는 "단락"이 빈 줄로 구분되지 않은 경우에도 작동한다는 의미입니다. (그러나 해당 태그 외부에 패턴과 일치하는 "스트레스" 텍스트가 있으면 해당 텍스트도 일치됩니다.)

찾고 있는 정규식을 / ... /프로그램의 일부에 넣을 수 있습니다( sed주소 문처럼). 하지만 고정된 문자열을 찾고 있다면 다음을 권장합니다.

awk -v RS="<|/>" 'index($0,"2020")' input.txt

대신에.

답변3

올바른 형식의 XML 문서가 다음과 같다고 가정합니다.

<root>
<thing  year="2019"
        month="1"
        day="1" />
<thing  year="2020"
        month="5"
        day="13" />
<thing  year="2021"
        month="7"
        day="3" />
</root>

다음 명령을 사용하여 thing속성에 값이 있는 각 노드의 복사본을 추출할 수 있습니다.2020yearxmlstarlet

$ xmlstarlet sel -t -c '//thing[@year = "2020"]' -nl file
<thing year="2020" month="5" day="13"/>

노드 내부와 해당 속성 사이의 공백은 문서의 내용과 아무 관련이 없습니다.

답변4

Raku(이전 Perl_6) 사용

이 스레드의 다른 답변에서 영감을 얻은 두 가지 답변은 다음과 같습니다. 첫 번째 답변은 @terdon 및 @AdminBee에서 영감을 받은 단락으로 구분되며, greps는 올바른 연도를 나타냅니다.

raku -e 'slurp.split("\n\n").grep(/2020/).put;' 

결과:

<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>

Larry Wall에 따르면 Raku는 언어 내에서 더 많은 작업을 쉽게 수행할 수 있는 기능을 제공하여 특수 명령줄 스위치에 대한 의존도를 줄입니다. "팁 #2"를 참조하세요:

https://www.nntp.perl.org/group/perl.perl6.users/2020/07/msg9004.html

두 번째 방법은 Raku의 루틴을 사용하는 것입니다 comb. 정규식 "매처"를 사용하여 텍스트를 일치 항목 이외의 요소로 나눕니다(추가 처리에 유용함). Raku 문서에서는 다음과 같이 설명합니다 comb.$matcher" 가장 겹치지 않는 일치 항목을 검색하고 반환합니다 ."$inputSeq$limit

raku -e '.put for slurp.comb(/^^ "<This" .*? "/>" $$ / ).grep(/2020/);' 

결과:

<This is a line of text with a year=2020 month=12 in it
This line of text does not have a year or month in it
This year=2021 is the current year the current month=1
This is the year=2021 the month=2/>

위의 코드는^^ 줄의 시작주장과 그 이후$$ 줄 끝역설. 기본적으로 .도트 와일드카드는 Raku의 공백(줄바꿈 포함)과 일치하므로 comb위의 내용은 텍스트를 여러 줄 블록(요소)으로 나눌 수 있습니다.

분명히 실제 XML 문서에 대한 가장 만족스러운 결과는 커뮤니티 지원 모듈이 포함된 Raku XML와 같은 전용 도구 및/또는 라이브러리를 사용하는 것입니다.XML

https://github.com/raku-community-modules/XML
https://raku.org/

관련 정보