2개의 태그/패턴 사이에서 처음 n번 발생하는 sed 결과 집합

2개의 태그/패턴 사이에서 처음 n번 발생하는 sed 결과 집합

큰 XML 파일이 있고 두 태그 사이에서 발생하는 모든 이벤트를 얻습니다.

내가 한 일은 다음과 같습니다.

sed -n '/<tag>/,/<\/tag>/p' file.xml

처음 N개 항목만 가져오도록 필터링해야 합니다. l param을 시도했지만 충분하지 않습니다 :(

그렇다면 모든 결과 세트에서 N개의 일치하는 이벤트를 얻는 방법을 아는 사람이 있습니까?

예를 들어. 다음은 xml 파일 내용입니다.

<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
</root>

 sed -n '/<tag>/,/<\/tag>/p' file.xml 

모든 요소를 ​​반환합니다.

따라서 목표는 n = 2인 경우 상위 n개의 일치 패턴(요소는 여러 줄)을 얻기 위해 필터링하는 것입니다. 그러면 결과는 다음과 같습니다.

<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>

답변1

노력하다:

xmllint --xpath '//tag[position()<=2]' file.xml

또는:

xmlstarlet sel -t -c '//tag[position()<=2]' file.xml

또는:

xmlstarlet sel -t -m '//tag[position()<=2]' -c . -n file.xml

이것을 사용하고 싶다면 sed다음과 같이 할 수 있습니다.

sed -n '
  1{x;s/^/../;x;}; # initialise counter with two tokens
  /<tag>/,/<\/tag>/ {
    p; /<\/tag>/{
      x;s/.//;/./!q;x; # remove a token and quit if hold space empty
    }
  }' file.xml

즉,예비 공간표시할 나머지의 카운터로(점 문자 사용)

답변2

이를 위해서는 파서를 사용해야 하지만, 아시다시피 모든 요소를 ​​인쇄하기 sed -n '/<tag>/,/<\/tag>/p' file.xml때문에 모든 요소를 ​​가져옵니다 . 이 명령은 p입력에 포함된 줄 과 포함된 다음 줄 <tag>사이의 모든 줄을 지정 하여 작동합니다 </tag>. 이것이 거의 모든 라인을 구성하므로 p인쇄하는 것만으로는 큰 차이가 나타나지 않습니다. 다음과 같은 것이 목표에 더 가까울 수 있습니다.

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}'

<tag>행의 주소를 지정하고 해당 행을 확인합니다 . </tag>끝 문자열이 포함되어 있지 않으면 다른 행을 가져오고 패턴 공간이 포함될 때까지 반복됩니다 <tag>.*</tag>[^\n]*$.

그런 다음 \n패턴 공간의 모든 줄 바꾸기 문자를 공백으로 변환합니다.

여기 다시 있습니다:

sed -n '\|<tag>|{:n;\|</tag>|!{N;bn};y|\n| |;p}' <<\DATA
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
</root>
DATA

산출:

<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>

이제 다음과 같이 할 수 있습니다:

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}' ./file | 
sed 's|> |>\n|g;2q'

...그것은 나에게 다음을 제공합니다:

<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>

답변3

내 생각엔 이게 네가 원하는 거야

sed -n '/<tag>/,/<\/tag>/p' file.xml | head -10

다음 명령을 사용하여 <tag>처음 두 줄을 가져오십시오.

$ sed -n '/^<tag>/p' file.xml | head -2
<tag><t1>john</t1></tag>
<tag><t1>john</t1></tag>

답변4

내가 아는 한 sed일치는 항상 탐욕적입니다. 즉, 그 사이의 다른 XML 객체를 포함하여 /<tag>/,/<\/tag>/첫 번째 인스턴스부터 마지막 ​​인스턴스까지 <tag>일치하는 것입니다 .<\tag>

귀하의 버전이 다중 문자 레코드 구분 기호를 지원하는 경우 awk다음과 같은 작업을 수행할 수 있습니다.

awk -v n=2 'BEGIN{RS="</tag>\n";ORS=RS} NR<=n'

그러나 훨씬 더 강력한 솔루션은 전용 XML 파서를 사용하는 것입니다. 예를 들어 Python을 사용한 매우 간단한 구현입니다.minidom

#!/usr/bin/python

from xml.dom import minidom

xmldoc = minidom.parse('file.xml')
taglist = xmldoc.getElementsByTagName('tag')
for i in range(2) :
        print taglist[i].toxml()

관련 정보