Como trocar blocos html inteiros em vários arquivos com sed

3

Eu tenho toneladas de páginas HTML com um conteúdo semelhante ao seguinte:

<div class="wrapper">

<div class="aaa">
...lot of html1 here like div's/spans etc..
</div> <!-- end aaa -->

<div class="bbb">
...lot of html2 here
</div><!-- end bbb -->

</div>

Eu preciso trocar div com as classes aaa e bbb inteiramente com seu conteúdo:

Então se torna:

<div class="wrapper">

<div class="bbb">
...lot of html2 here
</div><!-- end bbb -->

<div class="aaa">
...lot of html1 here
</div> <!-- end aaa -->

</div>

NOTAS:

1) Linhas vazias entre os blocos são opcionais.
2) Os HTMLs contêm apenas um par exato de aaa e bbb Assim, o caso: aaa then bbb then aaa não é possível.

Então você poderia, por favor, informar quais argumentos devo passar para sed . Estou usando find para encontrar todos os arquivos html e, em seguida, lançar sed como exec param:

find . -iname "*.html" -exec sed -i '' 's/WHAT IS HERE / AND HERE /g' {} \;

Se não for possível com sed , mas digamos que seja possível, com awk , ficarei feliz em compartilhar sua opinião sobre a melhor maneira de alcançar o desejado.

ATUALIZAÇÃO:

Aqui está o link para o exemplo da vida real: link

    
por Sray 24.12.2014 / 16:29

4 respostas

1

Se você quiser colocar a seção aaa de antes de bbb logo após:

sed -i '/<div class="aaa">/{
        :1
        /<\/div> <\!-- end aaa -->/!{N;b 1}
        /<\/div> <\!-- end aaa -->/{N;h}
        d}
        /<\/div><\!-- end bbb -->/{n;G}' *html
    
por 24.12.2014 / 17:07
1

Aqui está outro sed :

sed '/.*<div class="...">.*/{ h;s///;x;:n
     /<.div>/!N;/<!-- end/x;/<.div>/x;//!bn
    s/\(.*\).\(<div class=.*>\).*//;x
     /<.div>[^>]*$/s/.//;H;x
}'

A partir de uma linha class=.???. e passando por quantos blocos você tiver, para cada par, isso alterna suas posições. Então, aqui estão alguns exemplos:

Se sed encontrar uma linha que corresponda a:

<div class=".\{3\}">

... ao ler seu arquivo, ele garante que H old space esteja completamente livre e comece a puxar todas as linhas até encontrar uma linha que corresponda a:

<.div>

... e ...

<!-- end

... ou apenas o primeiro. Se corresponder a ambos, sed salvará o bloco em um buffer alternativo e puxará um segundo bloco antes de trocar suas posições na saída.

Se apenas o primeiro não afeta a posição dos blocos. Desta forma, os pares são deixados em paz.

Dado como entrada ...

<div class="wrapper">
<div class="aaa"> first </div> <!-- end aaa -->
between
<div class="bbb"> swap two </div> <!-- end bbb -->
blocks
<div class="ccc"> mismatched </div> <!-- end ccc --> 
the end         
</div>

Imprime ...

<div class="wrapper">
<div class="bbb"> swap two </div> <!-- end bbb -->
between
<div class="aaa"> first </div> <!-- end aaa -->
blocks
<div class="ccc"> mismatched </div> <!-- end ccc -->
the end
</div>

... se dado:

<div class="wrapper">
<div class="aaa"> first </div> <!-- end aaa -->
between
<div class="bbb"> swap two </div> <!-- end bbb -->
blocks
<div class="ccc"> matched </div> <!-- end ccc --> 
the end
<div class="ddd"> now matched </div> <!-- end ddd -->
</div>

Imprime ...

<div class="wrapper">
<div class="bbb"> swap two </div> <!-- end bbb -->
between
<div class="aaa"> first </div> <!-- end aaa -->
blocks
<div class="ddd"> now matched </div> <!-- end ddd -->
the end
<div class="ccc"> matched </div> <!-- end ccc -->
</div>

E, embora os exemplos estejam todos amassados assim por causa do espaço, não há uma preocupação real em saber se as seções <div class= begin e <.div> <!-- end estão ou não na mesma linha:

<div class="wrapper">
<div class="aaa">

the first
block is here

</div> <!-- end aaa -->

these lines were
between aaa and bbb

<div class="bbb">

this is the second block
it should be swapped with the first

</div> <!-- end bbb -->

more
blocks
follow

<div class="ccc"> this is matched </div> <!-- end ccc -->
not the end
<div class="ddd">

this last block
is matched with the ccc line
</div> <!-- end ddd -->

this is the end
</div>

Obtém ...

<div class="wrapper">
<div class="bbb"> 

this is the second block
it should be swapped with the first

</div> <!-- end bbb -->

these lines were
between aaa and bbb

<div class="aaa"> 

the first
block is here

</div> <!-- end aaa -->

more
blocks
follow

<div class="ddd"> 

this last block
is matched with the ccc line
</div> <!-- end ddd -->
not the end
<div class="ccc"> this is matched </div> <!-- end ccc -->

this is the end
</div>
    
por 25.12.2014 / 01:16
1

Isso não funciona para sed , a menos que você seja um glutão por punição. Pelo menos em um caso mais geral, em que o início do bloco é mais do que uma linha (ou seja, as tags são divididas em várias linhas, o que é bem possível em XML / HTML).

Se você realmente precisa fazer isso com qualquer coisa além de um analisador XML (sim, consertar a entrada ou cortar as partes quebradas geralmente seria uma ideia melhor), use algo como awk - é muito mais adequado para uma tarefa como essa *) . A ideia geral é:

  1. imprima linhas de entrada até o começo do primeiro bloco;
  2. acumular linhas do primeiro bloco para trocar;
  3. acumular linhas entre os blocos;
  4. linhas de impressão do segundo bloco;
  5. imprime linhas da peça entre os blocos acumulados no passo 3;
  6. linhas de impressão do primeiro bloco acumulado na etapa 2;
  7. imprima o resto.

Lembre-se também de verificar as canonical SO Q & A .

* Por que eu afirmo que: sed é orientado à linha e destina-se a simples ( sua milhagem pode variar ) transformações de texto. Embora isso seja verdade para o AWK (e em algum grau para o Perl), escrever scripts mais complexos é mais simples no último (acesso mais fácil a múltiplas variáveis, divisão automática em campos etc.). Assim, a menos que você só precise trocar dois blocos extremamente bem delimitados e nunca precise estender o script para manipular entradas formatadas de maneira diferente, uma linguagem mais complexa provavelmente será uma ferramenta melhor. Dito isso, o Perl tem um analisador XML prontamente disponível como um módulo.

    
por 24.12.2014 / 17:10
0

Analisar HTML com regex é claramente desencorajado.

Em vez disso, você pode usar & se seus arquivos de origem forem XHTML válidos:

xmlstarlet edit -L -u "//div[@class='a']" -v 'some inner HTML' file.xhtml

Se não for XHTML válido, tente adaptar o seguinte código perl:

use strict;
use warnings;
use 5.008;

use File::Slurp 'read_file';
use HTML::TreeBuilder;

sub replace_keyword
{
  my $elt = shift;

  return if $elt->is_empty;

  $elt->normalize_content;      # Make sure text is contiguous

  my $content = $elt->content_array_ref;

  for (my $i = 0; $i < @$content; ++$i) {
    if (ref $content->[$i]) {
      # It's a child element, process it recursively:
      replace_keyword($content->[$i])
          unless $content->[$i]->tag eq 'a'; # Don't descend into <a>
    } else {
      # It's text:
      if ($content->[$i] =~ /here/) { # your keyword or regexp here
        $elt->splice_content(
          $i, 1, # Replace this text element with...
          substr($content->[$i], 0, $-[0]), # the pre-match text
          # A hyperlink with the keyword itself:
          [ a => { href => 'http://example.com' },
            substr($content->[$i], $-[0], $+[0] - $-[0]) ],
          substr($content->[$i], $+[0])   # the post-match text
        );
      } # end if text contains keyword
    } # end else text
  } # end for $i in content index
} # end replace_keyword


my $content = read_file('foo.shtml');

# Wrap the SHTML fragment so the comments don't move:
my $html = HTML::TreeBuilder->new;
$html->store_comments(1);
$html->parse("<html><body>$content</body></html>");

my $body = $html->look_down(qw(_tag body));
replace_keyword($body);

# Now strip the wrapper to get the SHTML fragment back:
$content = $body->as_HTML;
$content =~ s!^<body>\n?!!;
$content =~ s!</body>\s*\z!!;

Emprestado de link

    
por 24.12.2014 / 16:45

Tags