特定のパターンの後に最初に見つかったパターンを含む行を削除する方法

特定のパターンの後に最初に見つかったパターンを含む行を削除する方法

特定のグループのエントリを削除するシェルスクリプトを作成しています。例: ファイル名は次のとおりです。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

そのようなノードが1つだけ存在し、見つかったらすぐに処理を中止するには、上記を追加してください;exitprint

おすすめ記事