Como deletar uma linha contendo o padrão que é encontrado primeiro após um padrão específico

0

Estou escrevendo um script de shell para excluir uma entrada de um grupo específico. exemplo: o nome do arquivo é 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>

Eu quero excluir uma entrada do arquivo acima (filename is dest.xml) com node id="PQR" de group_name="group1" (ele não deve ser excluído de group2 e group3). Eu posso fazer isso lendo um arquivo sequencialmente e depois deletá-lo de um grupo específico. Mas se o arquivo for grande (> 10k linhas), então leva tempo.

Existe alguma maneira simples para isso?

    
por Pravin Nagare 20.02.2013 / 15:46

2 respostas

1

Eu usaria awk se o formato não divergir da amostra que você forneceu:

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

para remover o nó "PQR" no domínio "group1".

$ 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">

Se você quiser remover esse nó do arquivo XML, isso não é realmente possível. Você precisaria pelo menos reescrever a parte que está depois desse nó para mudar os dados em bytes para trás.

Como alternativa, você poderia substituir esse nó por espaços em branco, o que significaria que você poderia se safar com a substituição apenas desses bytes.

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

Adicione um ;exit após o print acima, se houver apenas um desses nós e você quiser interromper o processamento assim que o encontrar.

    
por 20.02.2013 / 16:25
0

Eu escrevi um script python rápido, não sei se é simples o suficiente.

Coloque este script na mesma pasta com o seu 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

E agora é a versão do 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
}

Executar este script awk com argumento é o nome do arquivo dest.xml . Parece que é mais simples que a versão em python.

    
por 23.04.2013 / 13:00