I am trying to use sed to search for the occurrence of an exact string that contains / in it. Below is the approach I am trying, but without luck.

input_string="/path /path1 /path2"
input_string=echo $input_string | sed "s#\b$search_string\b##g"

But when I run these sequence of commands, I am still input_string to contain the value "/path /path1 /path2" instead of "/path1 /path2"

I even tried input_string=echo $input_string | sed "s#\<$search_string\>##g" but without any luck

I am using RHEL6.5 and running this command from shell.

  • 5
    What shell is this? AFAIK an assignment like input_string=echo blah isn't legal (you need a command substitution like $( ... ) or backticks. Aside from that, I suspect the issue is that / is a non-word character, so that there is no word boundary before it - do you get the desired result if you remove the initial \b? FYI you may be able to do what you want more simply using shell parameter substitution. – steeldriver Dec 30 '16 at 14:29
  • 1
    I suppose input_string is set with input_string=$( ), beside there is no leading blank in your test case. – Archemar Dec 30 '16 at 14:30
  • No shell handy, but I assume that with "...\b..." the shell will already try to parse that as an escape sequence, so sed doesn't even see it. Do you mind trying if "...\\b..." works better? – Ulrich Schwarz Dec 30 '16 at 14:46
  • echo ${input_string##$search_string} will strip leading $search_string (but not first space, hence I didn't post as answer). – Archemar Dec 30 '16 at 14:51
  • 1
    @Archemar how about ${input_string##$search_string }? – steeldriver Dec 30 '16 at 15:11

The problem here is the / in the pattern in $search_string.

Since / is not an alphanumeric character, it's not considered a "word". This means that the pattern \</path\> would not match /path since there is no word boundary before the /.

Change the search string to /path\> (i.e. s##$search_string\>##g) instead, or use /path[^0-9], or, with these specific input data and search string, you could simply use s#$search_string ##g as the substitution.


short answer

use this command to get what you want :

input_string=$(echo $input_string | sed "s#$search_string\b##g")

Long answer

Why sed isn't replacing

\b means a word boundary, and here is it's definition :

The metacharacter \b is an anchor like the caret and the dollar sign. It matches at a position that is called a "word boundary". This match is zero-length.

There are three different positions that qualify as word boundaries:

  • Before the first character in the string, if the first character is a word character.
  • After the last character in the string, if the last character is a word character.
  • Between two characters in the string, where one is a word character and the other is not a word character.

Found it here. So, the first character of your search_string is a /, and it seems that sed can't find a word boundary because of the first rule (because the / isn't a word character).

Why the result isn't stored in input_string

you need to first execute the two commands (echo and sed), then store the result in the variable input_string. To do this, put the two commands inside $(). This will make them execute and return the result to your variable (see the command in the short answer).

I know it's not a complete answer, but that's all I got.


Exact word matching in sed is tricky, especially if you want to write a portable solution, or if your definition of a "word" contains special characters other than _ (as in your situation). The best way I have found to do this is to consider all 4 cases where a word might appear in a line of input:

  1. At the beginning
  2. At the end
  3. At both the beginning and the end (occupying the entire line)
  4. In the middle somewhere

If your definition of a word is simply a sequence of non-whitespace characters, then the following solution should work for you (tested):

_s='[ ]' # contains a space and a literal tab character (CTRL-V + TAB) input_string=`echo "$input_string" | sed \ -e "s|^$search_string\\($_s\\)|\\1|g" \ -e "s|\\($_s\\)$search_string$|\\1|g" \ -e "s|^$search_string$||g" \ -e "s|\\($_s\\)$search_string\\($_s\\)|\\1\\2|g"`

Note that I have chosen | as the delimiter instead of #. This is because # can show up in file names (for example, Emacs auto-save files) while | is reserved, at least on Linux. Also note that the output will contain the whitespace character(s) surrounding each instance of $search_string. If you would rather omit the whitespace, you can use this instead (tested):

_s='[ ]' # contains a space and a literal tab character (CTRL-V + TAB) input_string=`echo "$input_string" | sed \ -e "s|^$_s*$search_string$_s\\{1,\\}||g" \ -e "s|$_s\\{1,\\}$search_string$_s*$||g" \ -e "s|^$search_string$||g" \ -e "s|\\($_s\\)$search_string$_s\\{1,\\}|\\1|g"`

That last expression looks a little different, and for good reason -- you don't want to remove whitespace on both sides of $search_string once you've gotten this far in processing the input line, because in doing so, you would remove whitespace between unmatched words on either side of $search_string, which would smoosh them together.

Final notes:

  • You might be able to get away with omitting some of the g modifiers in the sed expressions, but it doesn't hurt to leave them in.
  • It probably goes without saying, but if your $search_string is a path, it cannot contain any space.

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.