So ersetzen Sie eine Zeile in einer Eingabedatei mit sed/awk/perl

So ersetzen Sie eine Zeile in einer Eingabedatei mit sed/awk/perl

Ich versuche, einen Pfad in einer Eingabedatei zu ersetzen.

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

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

Nach dem Lesen der zuvor beantworteten Frage (https://stackoverflow.com/questions/11245144/replace-whole-line-containing-a-string-using-sed).

Ich habe das versucht.

    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

Ich glaube, sed/perl geraten durcheinander, aber ich bin nicht sicher, wie ich das lösen kann. Für jede Hilfe bin ich dankbar.

Antwort1

tl,dr:

Verwenden:

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

Einige Kommentare zu Ihrem versuchten Sed-Befehl:

sed -i '/../../../Plumed.h/c\/usr/local/include/Plumed.h' ../dist0.xvg
  • -iist nicht portierbar, daher sieht es so aus, als ob Sie GNU Sed verwenden.

    BSD Sed verfügt -iebenfalls über einen Schalter, aber die Backup-Erweiterung ist ein erforderliches Argument. Um also eine Datei zu ändern, ohne ein Backup in BSD Sed zu speichern, ist erforderlich -i ''.

    Andere Versionen von Sed haben möglicherweise -iüberhaupt keinen Schalter.

  • Der reguläre Ausdruck hier /../../../Plumed.h/enthält mehrere Kopien des Regex-Trennzeichens.

    Die übliche Lösung hierfür besteht darin, das Trennzeichen zu maskieren:

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

    Es gibt jedoch eine wenig bekannte Tatsache über Sed, die Sie verwenden könnenbeliebigZeichen für das Regex-Trennzeichen (nicht nur im sBefehl), wenn Sie die erste Instanz mit einem Backslash maskieren. (Also, fast jedes – Backslash oder Zeilenumbruch sind nicht zulässig.)

    Um denPOSIX-Spezifikationendirekt:

    In einer Kontextadresse wird die Konstruktion „ \cBREc“ verwendet, wobeiCist ein beliebiges Zeichen außer <backslash>oder <newline>, muss identisch sein mit " /BRE/". Wenn das durchCerscheint nach einem <backslash>, dann wird es als das wörtliche Zeichen betrachtet, das den BRE nicht beenden soll. Beispielsweise in der Kontextadresse " \xabc\xdefx" ist das zweiteXsteht für sich selbst, so dass das BRE „ abcxdef“ ist.

    Um das „Syndrom der schiefen Zahnstocher“ zu vermeiden, beachten Sie, dass die folgenden beiden regulären Ausdrücke gleichwertig sind:

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

    Beachten Sie, dass ich "äquivalent" gesagt habe.nichtrichtig. Das bringt mich zu meinem nächsten Punkt, der in jeder anderen Antwort fehlt:

  • Ein Punkt ( .) in einem regulären Ausdruck steht fürbeliebiges Zeichen.

    Wenn Sie nur einen wörtlichen Punkt abgleichen möchten, können Sie Ihren Punkt entweder mit einem Backslash maskieren oder ihn wie in in eine Zeichenklasse einfügen [.].

    Um nur wörtliche Punkte abzugleichen, sollte der reguläre Ausdruck also eher wie folgt lauten:

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

    Soviel zum Thema Vermeiden schief stehender Zahnstocher.

    Sie können auch die vermutlich besser lesbare Form verwenden:

    \:[.][.]/[.][.]/[.][.]/Plumed[.]h:
    
  • Der cBefehl ändert diegesamte Linie, nicht nur der Teil der Zeile, der mit dem regulären Ausdruck übereinstimmt.

    Verwenden Sie den sBefehl, um nur einen Teil einer Zeile zu ändern.

    Insbesondere smüssen Sie mit dem Befehl Ihr erstes Regex-Trennzeichen nicht maskieren (auch nicht, wenn es sich um ein ungewöhnliches Zeichen als Trennzeichen handelt), so wie Sie es tun, wenn Sie in einer Adresse ein alternatives Trennzeichen verwenden.

    In Bezug auf den cBefehl ist es außerdem wichtig zu wissen, dass das Einfügen des neuen Textes in dieselbe Zeile wie das c\eine GNU-Erweiterung ist und nicht portierbar.


Wenn Sie dies alles zusammenfassen, können Sie den sBefehl folgendermaßen verwenden:

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

Oder, wenn Sie es noch deutlicher machen möchten, können Sie einen verankerten regulären Ausdruck und den cBefehl „hange“ verwenden, um nur die ganze Zeile zu ändern:

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

Antwort2

/Wenn Sie als Trennzeichen mit verwenden sed, könnte eine Lösung wie folgt aussehen:

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

oder Sie können ein anderes Trennzeichen als verwenden /, zum Beispiel ::

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

Eine andere Möglichkeit mit awk:

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

Antwort3

Dieser Befehl hat bei mir funktioniert:

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

Antwort4

Verwenden Sie ein Ausrufezeichen, wenn der Text viele Schrägstriche enthält:

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

verwandte Informationen