Como inserir o conteúdo de um arquivo em outro arquivo entre duas linhas correspondentes?

2

Eu tenho um arquivo xml process.xml e quero inserir o conteúdo de tmp.xml nele. Mas a ressalva é que preciso que esses conteúdos sejam inseridos entre dois padrões de correspondência. Aqui está um trecho do arquivo process.xml

$cat process.xml
...
<fork name="data">
      <path start="process_x" />
      <path start="process_y" />
      <path start="process_z" />
</fork>
...
...
<action name="process_x" />
....
....
</action>
<action name="process_z" />
....
....
</action>

E aqui está o conteúdo do arquivo tmp.xml

$ cat tmp.xml
<path start="process_a" />
<path start="process_b" />

Meu padrão de correspondência será " process_z " e " </fork> " e o conteúdo deve ser colado entre esses padrões. Aqui está o que eu tentei ..

string=$(tac process.xml | grep -m1 -oP '(?<=path start="process_).*(?=" />)')
search="process_$string"
sed -e "/$search/ r tmp.xml" "process.xml"

Mas insere o conteúdo tmp.xml dentro do fork e action . No entanto, eu preciso apenas dentro do fork assim.

...
<fork name="data">
      <path start="process_x" />
      <path start="process_y" />
      <path start="process_z" />
      <path start="process_a" />
      <path start="process_b" />
</fork>
...
...

Qualquer ajuda é muito apreciada.

    
por Alex Raj Kaliamoorthy 15.11.2016 / 17:38

2 respostas

3

Parece que você deseja inserir o conteúdo desse arquivo após a última ocorrência de <path start="process_ .

Você poderia fazer:

awk '
  /path start="process_/ {print saved $0; saved=""; n++; next}
  n {saved = saved $0 RS; next}
  {print}
  END{system("cat tmp.xml"); printf "%s", saved}' process.xml

Embora isso signifique armazenar a parte do arquivo da última ocorrência de path start="process_ para o final na memória.

Ou você pode sorver os arquivos inteiros na memória com:

perl -0777 -pe 's/.*path start="process_.*?\n\K/<STDIN>/se
               ' process.xml < tmp.xml

Uma variante que verifica o </fork> na próxima linha não vazia:

perl -0777 -pe 's{.*path start="process_[^\n]*\n\K(?=\s*</fork>)}{<STDIN>}se
               ' process.xml < tmp.xml

Uma variante que alinha o recuo e adiciona um caractere de nova linha extra se estiver faltando em tmp.xml :

perl -0777 -pe 's{(?s:.*)(^\h*).*path start="process_.*\n\K(?=\s*</fork>)}{
 $insert = <STDIN>;
 $indent = $1;
 $insert =~ s/^/$indent/gm;
 $insert =~ s/\n?$/\n/;
 $insert}me' process.xml < tmp.xml

Com -0777 -pe 'code' file , perl executa o code , com $_ sendo o conteúdo do file e imprime esse $_ (aqui modificado pelo code ) posteriormente.

Lá, temos apenas um comando de substituição s{pattern}{replacement}flags .

O truque para obter a última ocorrência do padrão em todos esses comandos é o principal ganancioso .* (aqui sob o s flag, para que ele também corresponda aos caracteres de nova linha). Como ele é ganancioso, ele tentará corresponder tantos caracteres até ^ (um começo de linha com o m flag) seguido por uma sequência de espaços em branco horizontais ( \h* ), que capturamos em $1 with (\h*) seguido por nosso padrão seguido pelo resto da linha ( .* desta vez sem o s flag, então não irá engolir os caracteres de nova linha) seguido por uma nova linha.

Depois disso, adicionamos um \K para informar perl que é o início do texto correspondente. Em seguida, temos um operador de look ahead para verificar se essa nova linha é seguida por uma sequência de espaço em branco ( \s* ) e </fork> .

Na substituição, nós tomamos o conteúdo de tmp.xml do stdin e inserimos o recuo capturado no início de cada uma de suas linhas, adicionamos a nova linha à direita se estiver faltando e oferecemos isso como substituto.

Uma alternativa poderia ser processar o arquivo duas vezes. Uma vez para recuperar o número da linha da última ocorrência do padrão, e o segundo para inserir o arquivo lá:

sed "$(awk '/path start="process_/{n=NR};END{print n}' < process.xml  
      )r tmp.xml" process.xml

Como alternativa, talvez você possa simplesmente inseri-lo antes de </fork> :

awk '/<\/fork>/{system("cat tmp.xml")};1' < process.xml
    
por 15.11.2016 / 18:26
0

Supondo que haja apenas um <fork> no seu arquivo ...

Adicione </fork> ao final da sua string de inserção.

Substitua </fork> pela sua string de inserção modificada.

Atualização: como as tags de ação também contêm o mesmo nome de processo, expanda a string 'search' para encontrar a tag inteira <path start="process_$string" /> . Ou o suficiente para eliminar as correspondências de 'ação', assim: t="process_$string"

    
por 15.11.2016 / 18:32