Questão Substituindo um padrão de várias linhas em um arquivo HTML


Eu tenho uma série de arquivos HTML que contêm duas linhas como esta:

<body>
<h1>Title</h1><p>
<a href="url">Description</a><br>

Eu quero substituir este texto com outra coisa usando um script bash. estou tentando

sed -i -r 's/<h1>Title.*?$\/^.*?<br>/Replacement text/1' filename.html

Mas isto não está funcionando. Eu estou suspeitando que está ficando preso na nova linha e não sabendo como contornar o problema.

Qualquer ajuda apreciada. Sinta-se à vontade para sugerir outras ferramentas do Linux que não sejam sed enquanto funcionar!


4


origem


Você realmente precisa sed por esta? sed lê linha por linha, então substituir o texto de várias linhas é um pouco entediante lá. - slhck
Eu não tenho que usar sed. Estou aberto a outros comandos do Linux. - To Do
sim, um padrão cruzando várias linhas pode ser um problema para o sed. - barlop


Respostas:


Eu usaria o Perl para isso:

perl -0pe 's/<h1>Title.*\n.*<br>/replacement/' filename.html

Aqui, -0 faz Perl dividir registros no NUL caractere em vez de ler linha por linha, que é o padrão quando se usa o -p opção.

Com expressões regulares Perl você precisa .*  para coincidir com qualquer caractere várias vezes, e você corresponde a nova linha com \n.

Exemplo:

$ echo '<body>
<h1>Title</h1><p>
<a href="url">Description</a><br>' | perl -0pe 's/<h1>Title.*\n.*<br>/replacement/'
<body>
replacement

9



E não esqueça nunca use expressões regulares para analisar HTML! :) - terdon
Isto é quase Boa. Eu adicionei um? depois dos dois. * para desabilitar a ganância. É possível fazer o comando modificar o arquivo em vez de enviar para o STDOUT? - To Do
@ToDo Perl pode fazer edição no local com o -i opção, então corra perl -0p -i~ -e 's/…/…/' para editá-lo, criando uma cópia de backup com ~ como sufixo. Ou, para substituir sem o arquivo de backup, chame perl -0pi -e …. - slhck


sed não pode corresponder mais de uma linha diretamente. Quando o padrão multilinha é necessário, alcance uma ferramenta mais poderosa como o Perl:

perl -i~ -ne 'if (/^<h1>Title/) {
                  $n = <>;
                  if ($n =~ /<br>$/) { print "Replacement\n" }
                  else { print "$_$n" }
              } else { print }'

1





Isso pode ser feito com sed.

sed -nf repl.sed filename.html

Onde repl.sed contém:

# Must have one line loaded up before branching to rep.
# Processing will start this way.
:rep
# Load extra line into pattern space
N
# Test for title
/<h1>.*<\/h1><p>\n<a href=".*">.*<\/a><br>/{
  #Substitute and print
  s/<h1>\(.*\)<\/h1><p>\n<a href=".*">.*<\/a><br>/Title: \1/p
  #append next line without cycling
  N
  # everything but the last line
  s/.*\n\([.\n]*\)/\1/
  #test for last line
  ${
    p
    # this will effectively end the program
    n
  }
  b rep
}
${
  # will print pattern space (both lines)
  p
  # this will effectively end the program
  n
}
#Print first line in pattern space
P;
#Remove first line in pattern space with newline
s/.*\n\([.\n]*\)/\1/
b rep

Vejo Trabalhando com várias linhas


1