특정 패턴 다음에 처음 발견된 패턴이 포함된 줄을 삭제하는 방법

특정 패턴 다음에 처음 발견된 패턴이 포함된 줄을 삭제하는 방법

특정 그룹의 항목을 삭제하는 쉘 스크립트를 작성 중입니다. 예: 파일 이름은 다음과 같습니다.dest.xml

<domain id="1" group_name="group1">
    <node id="ABC">
    <node id="PQR">
    <node id="XYZ">
</domain>
<domain id="2" group_name="group2">
    <node id="PQR">
    <node id="XYZ">
</domain>
<domain id="3" group_name="group3">
    <node id="ABC">
    <node id="PQR">
 </domain>

위 파일(파일 이름은 dest.xml)에서 항목을 삭제하고 싶습니다 node id="PQR"( group_name="group1"group2 및 group3에서는 삭제하면 안 됩니다). 파일을 순차적으로 읽은 다음 특정 그룹에서 제거하면 됩니다. 하지만 파일이 너무 크면(>10,000줄) 시간이 걸립니다.

쉬운 방법이 있나요?

답변1

awk형식이 제공한 예와 다르지 않으면 다음을 사용합니다.

awk -F'[<>="[:blank:]]+' '
  $2 == "domain" {group = $(NF-1)}
  !(group == "group1" && $2 == "node" && $(NF-1) == "PQR")
  ' < dest.xml > new-dest.xml

"group1" 도메인에서 "PQR" 노드를 삭제합니다.

$ diff -u dest.xml new-dest.xml
--- dest.xml    2013-02-22 07:01:48.732227421 +0000
+++ new-dest.xml        2013-02-22 07:02:16.111512820 +0000
@@ -1,6 +1,5 @@
 <domain id="1" group_name="group1">
     <node id="ABC">
-    <node id="PQR">
     <node id="XYZ">
 </domain>
 <domain id="2" group_name="group2">

XML 파일에서 노드를 제거하려는 경우에는 불가능합니다. 데이터를 최대한 많은 바이트로 다시 이동하려면 최소한 해당 노드 뒤의 부분을 다시 작성해야 합니다.

또는 노드를 공백으로 바꿀 수 있습니다. 즉, 해당 바이트만 바꿀 수 있습니다.

perl -ne '
  if (/<domain.*group_name="(.*?)"/) {
    $in = $1 eq "group1"
  } elsif ($in && /<node id="PQR"/) {
    s/./ /g;
    seek STDOUT,tell(STDIN)-length$_,0;
    print
  }' < dest.xml 1<> dest.xml

그러한 노드가 하나만 있고 발견되는 즉시 처리를 중지하려면 위에 추가하십시오 ;exit.print

답변2

나는 빠른 Python 스크립트를 작성했지만 충분히 간단한지 확실하지 않습니다.

이 스크립트를 dest.xml.

#!/usr/bin/python
import re
FILENAME = 'dest.xml'
GROUPNAME = 'group1'
NODEID = 'PQR'

with open(FILENAME) as f:
    in_group = False
    for line in f:
        line = line.strip()
        group_pattern = 'group_name="{0}">'.format(GROUPNAME)
        end_group_pattern = '</domain>'
        node_pattern = '<node id="{0}">'.format(NODEID)
        if re.search(group_pattern, line):
            in_group = True
        if re.search(end_group_pattern, line):
            in_group = False
        if re.search(node_pattern, line) and in_group:
            pass
        else:
            print line

이제 awk 버전입니다.

#!/usr/bin/awk -f
BEGIN {
    GROUPNAME = "group1"
    NODEID = "PQR"
    in_group = 0
    group_pattern =  ".*group_name=\"" GROUPNAME "\""
    end_group_pattern = "</domain>"
    node_pattern = "<node id=\"" NODEID "\">"
}
$0 ~ group_pattern {
   in_group = 1
}
$0 !~ node_pattern || in_group == 0 {
    print $0
}
$0 ~ end_group_pattern {
    in_group = 0
}

파일 이름을 인수로 사용하여 이 awk 스크립트를 실행하십시오 dest.xml. 파이썬 버전보다 더 간단한 것 같습니다.

관련 정보