32

I am having trouble figuring out how to use sed to search and replace strings containing the / character in a text file /etc/myconfig.

For instance, in my existing text file, I have:

myparam /path/to/a argB=/path/to/B xo

and I want this replaced by:

myparam /path/to/c argB=/path/to/D xo

I attempted doing this in bash:

line='myparam /path/to/a argB=/path/to/B xo'
line_new='myparam /path/to/c argB=/path/to/D xo'
sed -i 's/$line/$line_new/g' /etc/myconfig

But nothing happens.

Attempting

grep -rn "$line" /etc/myconfig

does return me 'myparam /path/to/a argB=/path/to/B xo' though.

What's the correct way to express my sed command to execute this search and replace and correctly deal with the / command? (I reckon that the / character in my strings are the ones giving me the problem because I used a similar sed command to search and replace another line in the text file with no problems and that line does not have a / character.

Calvin Cheng
  • 35,640
  • 39
  • 116
  • 167

2 Answers2

50

Don't escape the backslashes; you'll confuse yourself. Use a different symbol after the s command that doesn't appear in the text (I'm using % in the example below):

line_old='myparam /path/to/a argB=/path/to/B xo'
line_new='myparam /path/to/c argB=/path/to/D xo'
sed -i "s%$line_old%$line_new%g" /etc/myconfig

Also, enclose the whole string in double quotes; using single quotes means that sed sees $line (in the original) instead of the expanded value. Inside single quotes, there is no expansion and there are no metacharacters. If your text can contain almost any plain text character, use a control character (e.g. control-A or control-G) as the delimiter.

Note that the use of -i here mirrors what is in the question, but that assumes the use of GNU sed. BSD sed (found on Mac OS X too) requires a suffix. You can use sed -i '' … to replace in situ; that does not work with GNU sed. To be portable between the two, use -i.bak; that will work with both — but gives you a backup file that you'll probably want to delete. Other Unix platforms (e.g. AIX, HP-UX, Solaris) may have variants of sed that do not support -i at all. It is not required by the POSIX specification for sed.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • thanks! The double quote `"` and the `%` did the trick. I originally tried escaping the `/` character by doing `\/` and that did not work at all. – Calvin Cheng Apr 25 '12 at 05:57
  • Note that if you need to specify a range of lines by regex and the regex contains slashes, you can use `sed -n '\@dir/file@,\@dir2/file2@p' where the backslash before the `@` indicates that the `@` is the search delimiter for this expression. – Jonathan Leffler Dec 14 '14 at 16:02
  • Note that one may need `sed -i -e` – PhysicalChemist Aug 16 '16 at 20:04
  • @PhysicalChemist: Would you care to elaborate on your comment? The `-e` is optional unless you'll have multiple expressions (`sed -e 's/a/b/' -b 's/xy/ZZ/g'`) — AFAIK. Using `sed -i -e …` would make the script 'portable' between GNU `sed` and BSD `sed` at the cost of having BSD `sed` create a backup file with the extension `-e` (so if you edit file `data`, the backup file on Mac OS X will be `data-e`; GNU `sed` will not create a backup). I'm not sure that's a good enough reason to do things that way; it would be better to use `-i.bak` and remove the backup — that works with both variants. – Jonathan Leffler Aug 16 '16 at 22:03
  • This is the only solution works for me – Madiha Khalid Nov 10 '21 at 09:16
16

This might work for you:

sed -i "s|$line|$line_new|g" /etc/myconfig

You must use "'s so that the $line and $new_line are interpolated. Also use | or any character not found in the match or replacement strings as a delimiter.

potong
  • 55,640
  • 6
  • 51
  • 83
  • 1
    ok. I did not know about the `|` trick. So you mean to say that ANY delimiter will work as long as it is not a character found in the search or replace strings? – Calvin Cheng Apr 25 '12 at 05:58
  • Yes; the first character after the `s` is used to separate the command into two parts; the pattern to be matched and the replacement pattern. `s/search/replace/` uses `/` three times; but `s|search|replace|` or `s%search%replace%` works too. Perl allows matching brackets (`s{search}{replace}`) but `sed` does not (unless, perhaps, you have activated the Perl patterns option if you have GNU `sed`). – Jonathan Leffler Apr 25 '12 at 06:06
  • 1
    Yes, within reason. `|` is a handy one to use as it is not likely to appear in a file. However it is a metacharacter (used in alternation) so if you wanted to say `s/this\|that/those/` then use another delimiter such as `@` or `#` or `,` or `?` or `%` etc. Also note this is for the substitution command for the search command `/.../` to use an alternative delimiter preceed the alternative with a backslash e.g. `\|foo|` – potong Apr 25 '12 at 06:10
  • Thanks @JonathanLeffler I don't think GNU sed has a `Perl patterns option` perhaps you are thinking of `ssed` (super sed) a fork of GNU sed. – potong Apr 25 '12 at 06:18
  • It looks like you're right; it is GNU `grep` that has Perl regexes aboard, but not substitution operations. That's the difficulty with these things, keeping track of which commands have which non-standard extensions. – Jonathan Leffler Apr 25 '12 at 06:25