Como substituir uma linha em um arquivo de entrada usando sed/awk/perl

Como substituir uma linha em um arquivo de entrada usando sed/awk/perl

Estou tentando substituir um caminho em um arquivo de entrada.

    #include "../../../Plumed.h"         #### this is old patch in input 

    #include "/usr/local/include/Plumed.h  #### this should be the new path

Depois de ver a pergunta respondida anteriormente (https://stackoverflow.com/questions/11245144/replace-whole-line-containing-a-string-using-sed).

Eu tentei isso.

    sed -i '/../../../Plumed.h/c\/usr/local/include/Plumed.h' ../dist0.xvg


    perl -i -pe 's/../../../Plumed.h/usr/local/include/Plumed.h/g' ../dist0.xvg

Acredito que sed/Perl está ficando confuso, mas não tenho certeza de como superar isso. Qualquer ajuda será apreciada.

Responder1

tl, dr:

Usar:

sed -i 's:"\.\./\.\./\.\./Plumed\.h":"/usr/local/include/Plumed.h":g' ../dist0.xvg

Alguns comentários sobre sua tentativa de comando Sed:

sed -i '/../../../Plumed.h/c\/usr/local/include/Plumed.h' ../dist0.xvg
  • -inão é portátil, então parece que você está usando o GNU Sed.

    -iO BSD Sed também possui uma opção, mas a extensão de backup é um argumento obrigatório, portanto, para modificar um arquivo sem salvar um backup no BSD Sed, é necessário -i ''.

    Outras versões do Sed podem não ter nenhum -iswitch.

  • A regex aqui, /../../../Plumed.h/, contém múltiplas cópias do delimitador de regex.

    A solução usual para isso é escapar do delimitador:

    /..\/..\/..\/Plumed.h/
    

    No entanto, há um fato pouco conhecido sobre o Sed, que você pode usarqualquercaractere para o delimitador regex (não apenas no scomando) se você escapar com barra invertida na primeira instância. (Bem, quase qualquer barra invertida ou nova linha não é permitida.)

    Para citar oEspecificações POSIXdiretamente:

    Num endereço de contexto, a construção " \cBREc", ondecfor qualquer caractere diferente de <backslash>ou <newline>, será idêntico a " /BRE/". Se o caracter designado porcaparece após um <backslash>, então será considerado aquele caractere literal, que não encerrará o BRE. Por exemplo, no endereço de contexto " \xabc\xdefx", o segundoxrepresenta por si só, de modo que o BRE é " abcxdef".

    Portanto, para evitar a "síndrome do palito inclinado", observe que as duas expressões regulares a seguir são equivalentes:

    /..\/..\/..\/Plumed.h/
    \:../../../Plumed.h:
    

    Observe que eu disse "equivalente"nãocorreto. Isso me leva ao próximo ponto, perdido em todas as outras respostas:

  • Um ponto final ( .) em uma regex significaqualquer personagem.

    Se quiser corresponder apenas um ponto final literal, você pode escapar do ponto final com uma barra invertida ou inseri-lo em uma classe de caractere, como em [.].

    Assim, para corresponder apenas a períodos literais, a regex deveria ser mais parecida com:

    \:\.\./\.\./\.\./Plumed\.h:
    

    Tanto para evitar palitos inclinados.

    Você também pode usar a forma provavelmente mais legível:

    \:[.][.]/[.][.]/[.][.]/Plumed[.]h:
    
  • O ccomando altera olinha inteira, não apenas a parte da linha correspondida pela regex.

    Use o scomando para alterar apenas uma parte de uma linha.

    Notavelmente, com o scomando, você não precisa escapar do seu primeiro delimitador regex (mesmo para um caractere incomum usado como delimitador) da mesma forma que faz ao usar um delimitador alternativo em um endereço.

    Além disso, em relação ao ccomando, vale a pena saber que incluir o novo texto na mesma linha do c\é uma extensão GNU e não portátil.


Juntando tudo isso, você pode usar o scomando assim:

sed -i 's:"\.\./\.\./\.\./Plumed\.h":"/usr/local/include/Plumed.h":g' ../dist0.xvg

Ou, se quiser ser ainda mais explícito, você pode usar uma regex ancorada e o ccomando hange para alterar apenas a linha inteira:

sed -i '\:^#include "\.\./\.\./\.\./Plumed\.h"$:c\#include "/usr/local/include/Plumed.h"' ../dist0.xvg

Responder2

Se você estiver usando /como delimitador com sed, uma solução poderia ser:

sed -i 's/..\/..\/..\/Plumed.h/\/usr\/local\/include\/Plumed.h/g' file

ou você pode usar um delimitador diferente de /, por exemplo ::

sed -i 's:../../../Plumed.h:/usr/local/include/Plumed.h:g' file

Outra maneira com awk:

awk '{ gsub("../../../Plumed.h","/usr/local/include/Plumed.h"); print $0}' file

Responder3

Este comando funcionou para mim:

     sed -i 's#include "../../../Plumed.h"#include "/usr/local/include/Plumed.h"#g'

Responder4

Use ponto de exclamação se houver muitas barras no texto:

sed -e 's!../../../Plumed.h!/usr/local/include/Plumed.h!'

informação relacionada