0

I am trying to replace a path in an input file.

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

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

After seeing to previously answered question (https://stackoverflow.com/questions/11245144/replace-whole-line-containing-a-string-using-sed).

I tried this.

    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

I believe sed/perl is getting confused but I am not sure how to overcome this. Any help will be appreciated.

  • What previously answered question? Please be more specific. – phk Oct 30 '16 at 7:57
  • mentioned sir !! – Vikas Dubey Oct 30 '16 at 8:01
0

If you're using / as delimeter with sed, a solution could be:

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

or, you can use a different delimeter than /, for example ::

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

Another way with awk:

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

tl,dr:

Use:

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

A few comments on your attempted Sed command:

sed -i '/../../../Plumed.h/c\/usr/local/include/Plumed.h' ../dist0.xvg
  • -i is not portable, so it appears you are using GNU Sed.

    BSD Sed has a -i switch as well, but the backup extension is a required argument, so to modify a file without saving a backup in BSD Sed requires -i ''.

    Other versions of Sed may have no -i switch at all.

  • The regex here, /../../../Plumed.h/, contains multiple copies of the regex delimiter.

    The usual solution to this is to escape the delimiter:

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

    However, there is a little-known fact about Sed, that you can use any character for the regex delimiter (not just in the s command) if you backslash-escape the first instance. (Well, almost any—backslash or newline not allowed.)

    To quote the POSIX specifications directly:

    In a context address, the construction "\cBREc", where c is any character other than <backslash> or <newline>, shall be identical to "/BRE/". If the character designated by c appears following a <backslash>, then it shall be considered to be that literal character, which shall not terminate the BRE. For example, in the context address "\xabc\xdefx", the second x stands for itself, so that the BRE is "abcxdef".

    So to avoid the "leaning toothpicks syndrome," note that the following two regexes are equivalent:

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

    Note that I said "equivalent," not correct. This brings me to my next point, missed in every other answer:

  • A period (.) in a regex stands for any character.

    If you want to match only a literal period, you can either escape your period with a backslash, or stick it into a character class as in [.].

    Thus to match only literal periods, the regex should actually be more like:

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

    So much for avoiding leaning toothpicks.

    You can also use the arguably more readable form:

    \:[.][.]/[.][.]/[.][.]/Plumed[.]h:
    
  • The c command changes the entire line, not just the portion of the line matched by the regex.

    Use the s command to only change a portion of a line.

    Notably, with the s command, you don't have to escape your first regex delimiter (even for an unusual character used as a delimiter) the way you do when using an alternate delimiter in an address.

    Also, regarding the c command, it's worth knowing that including the new text on the same line as the c\ is a GNU extension and not portable.


Putting all this together, you can either use the s command like so:

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

Or, if you want to be even more explicit, you can use an anchored regex and the change command to change the whole line only:

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

This command worked for me :

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

Use exclamation point if there is many slashes in the text:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.