34

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.

2
  • 1
    The substitution and quoting rules are really about bash rather than sed specifically. You might want to add bash as a tag. Commented Apr 25, 2012 at 5:56
  • ok. will add bash as a tag. Commented Apr 25, 2012 at 5:59

2 Answers 2

53

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.

Sign up to request clarification or add additional context in comments.

11 Comments

thanks! The double quote " and the % did the trick. I originally tried escaping the / character by doing \/ and that did not work at all.
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.
@Milan — Well, Ubuntu 18 is old, very old. However, without seeing what you tried to execute, and what the error was, it is impossible to help you. You could ask a new question, mentioning this one (with a URL to it) as where you got your ideas from, and then show what doesn't work.
@Milan: I created a script containing: file_old="old.config"; file_new="new.config"; line_old='myparam /path/to/a argB=/path/to/B xo'; line_new='myparam /path/to/c argB=/path/to/D xo'; echo "$line_old" > "$file_old"; sed -e "s%$line_old%$line_new%g" "$file_old" > "$file_new"; echo "== $file_old =="; cat "$file_old"; echo "== $file_new =="; cat "$file_new" — Granted this doesn't use the -i option or scribble on a file in /etc, but the output was 4 lines == old.config ==myparam /path/to/a argB=/path/to/B xo== new.config ==myparam /path/to/c argB=/path/to/D xo.
@Milan: If you can't get that to work, contact me via email (see my profile) and include detailed information, including the output from sed --version. Include "Stack Overflow" in the subject and this question's URL in the body so I know who I'm talking to.
|
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.

5 Comments

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?
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).
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|
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.
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.