Como posso encontrar o símbolo n-th '' contendo a palavra em um arquivo de texto semelhante a XML?

2

Eu tenho um arquivo de texto semelhante a XML, que não pode ser analisado com um analisador de XML devido a violações de XML:

<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note> 

Eu só quero cortar a palavra depois de n th tag abrir < em um arquivo. O arquivo deve estar em XML-grammar, o que significa que as linhas podem variar.

Meu resultado esperado seria

1  -  note
2  -  to
3  -  /to
4  -  from
5  -  /from
6  -  heading
7  -  /heading
8  -  body
9  -  /body
10 -  /note
    
por Bhanuchander_U 15.02.2018 / 06:58

5 respostas

2
Solução

grep + awk :

grep -Eo '<[^<>]+>' input.xml | awk '{ gsub(/[<>]/,""); printf "%-3s - %s\n", NR, $0 }'

A saída:

1   - note
2   - to
3   - /to
4   - from
5   - /from
6   - heading
7   - /heading
8   - body
9   - /body
10  - /note

Ou com o comando único GNU awk :

awk -v FPAT='</?[^<>]+>' '{ for(i=1;i<=NF;i++) printf "%-3s - %s\n", ++c, $i }' input.xml
    
por 15.02.2018 / 08:31
3
$ awk -F"[<>]" '{for(i=2;i<=NF;i+=2){print ++j" - "$i}}' input.xml
1 - note
2 - to
3 - /to
4 - from
5 - /from
6 - heading
7 - /heading
8 - body
9 - /body
10 - /note
    
por 15.02.2018 / 10:10
3

Nota: Esta resposta foi escrita antes do usuário explicar que o XML não estava bem formado. Estou deixando aqui, pois isso pode ajudar os outros.

XMLStarlet é capaz de produzir a estrutura de elementos de documentos XML:

$ xml el file.xml
note
note/to
note/from
note/heading
note/body

Isso é diferente da saída esperada, mas pode ser suficiente para o que você deseja alcançar.

Também é capaz de converter o XML em PYX, que mostra as tags de abertura e fechamento em linhas separadas:

$ xml pyx file.xml
(note
-\n
(to
-Tove
)to
-\n
(from
-Jani
)from
-\n
(heading
-Reminder
)heading
-\n
(body
-Don't forget me this weekend!
)body
-\n
)note

Com isso, é fácil obter exatamente a saída desejada:

$ xml pyx file.xml | sed -n -e 's/^(//p' -e 's/^)/\//p'| nl
     1  note
     2  to
     3  /to
     4  from
     5  /from
     6  heading
     7  /heading
     8  body
     9  /body
    10  /note

As instruções sed eliminam as linhas que não iniciam com ( ou ) e substituem esses caracteres de acordo com a forma como você especificou na pergunta. O utilitário nl coloca o número da linha nas linhas.

Às vezes, o XMLStarlet é instalado como xmlstarlet em vez de xml .

    
por 15.02.2018 / 08:35
2

aqui é um método bastante fácil de responder à sua pergunta sobre extração de tags de abertura ... mas o seu exemplo pede também para o fechamento de ... isso parece um erro porque um fechado está aberto, é claro ... você realmente precisa também de uns fechados, mas se você quiser controlar o formato xml, mas o uso de uma ferramenta como xmllint ....

bash-4.4$ cat > toto
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note> 
bash-4.4$ awk '{
match($0,/<\/.*>/); 
b=substr($0,RSTART,RLENGTH); 
 if(b)
    {a[++i]=b}
     } 
END{
  {for(k in a)
    {c[a[k]]=k}
 } 
 {for(u in c)
  {gsub(/\//,X,u);print u}
 }
   }' toto | sed 's/</- /;s/>//' | cat -n
     1  - body
     2  - note
     3  - to
     4  - heading
     5  - from
bash-4.4$ rm toto

ou para manter todos os & usando sed apenas por diversão:

bash-4.4$ sed -e  's/>\(.*\)</></;s/>/\n/g;s/</- /g' toto | sed '/^$/ d' | cat -n
     1  - note
     2  - to
     3  - /to
     4  - from
     5  - /from
     6  - heading
     7  - /heading
     8  - body
     9  - /body
    10  - /note
    11   
bash-4.4$ 
    
por 15.02.2018 / 08:24
1

Aqui está uma solução XQuery apenas para o caso de você querer algo que funcione em QUALQUER XML, mesmo XML complicado contendo comentários, DTDs, elementos de fechamento automático, etc.

declare function local:f($e) {
  $e / (name(), local:f(*), ('/' || name()))
};
for $tag at $p in local:f(/*)
return ($p || ' - ' || $tag || '&#xa;')
    
por 15.02.2018 / 10:36