Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do alternate delimiters not work with sed -e '/pattern/s/a/b/'? [duplicate]

Tags:

sed

Here is something I have discover that doesn't make sense.

Output of cat config.h:

define somevar foo // Sample variable

This version of the command works to change foo to bar and preserve the comments:

sed -e '/somevar/s/foo/bar/' config.h

This doesn't work:

sed -e '|somevar|s|foo|bar|' config.h

Gives this error:

sed: -e expression #1, char 1: unknown command: `|'

Funnily enough this does work:

sed -e '/somevar/s|foo|bar|' config.h

Perhaps I am missing some part of the documentation. It seems very odd to have two different delimiters in the same sed command.

Bug or feature?

like image 890
codexmas Avatar asked Dec 27 '13 21:12

codexmas


People also ask

How do you replace special characters in sed?

You need to escape the special characters with a backslash \ in front of the special character. For your case, escape every special character with backslash \ .

Can sed match multiple lines?

By using N and D commands, sed can apply regular expressions on multiple lines (that is, multiple lines are stored in the pattern space, and the regular expression works on it): $ cat two-cities-dup2.

How do you replace a sed in Word?

Find and replace text within a file using sed command Use Stream EDitor (sed) as follows: sed -i 's/old-text/new-text/g' input.txt. The s is the substitute command of sed for find and replace. It tells sed to find all occurrences of 'old-text' and replace with 'new-text' in a file named input.txt.


5 Answers

This will work:

sed -e '\|somevar|s|foo|bar|'

The man page of GNU sed is pretty clear about this:

   /regexp/
          Match lines matching the regular expression regexp.

   \cregexpc
          Match lines matching the regular expression regexp.  The  c  may
          be any character.

That is, the c may be any character, but the starting \ is mandatory.

I don't have a FreeBSD around, but according to @bonsaiviking the man page there is also very clear:

The opening delimiter needs to be preceded by a backslash unless it is a slash.

On the other hand in OSX this is not clear at all:

      In a context address, any character other than a backslash (``\'')
      or newline character may be used to delimit the regular expression.
      Also, putting a backslash character before the delimiting character
      causes the character to be treated literally.  For example, in the
      context address \xabc\xdefx, the RE delimiter is an ``x'' and the
      second ``x'' stands for itself, so that the regular expression is
      ``abcxdef''.

Notice that the example there uses \xpatternx instead of just xpatternx. That's all the clue it gives, it doesn't make it clear that xpatternx won't work.

Based on the argument of @that-other-guy, it makes sense that sed (and other languages like perl as @Birei pointed out) need this extra clue to work correctly.

like image 165
janos Avatar answered Dec 19 '22 17:12

janos


Allowing different delimiters for /pattern/ would introduce parsing ambiguity.

Is ispaghetti supposed to be like /spaghett/, or is it supposed to insert text like i spaghetti?

With s and y there is no such ambiguity. When you see either of those characters, you know the command you're reading, and then you can interpret the next character as a delimiter.

We could resolve this ambiguity for /pattern/ if we started it with a similarly recognizable character, and indeed sed has a separate address specifier for this: backslash, as in \|pattern| (this is not the same as escaping).

We can therefore write \|pattern|s|foo|bar|.

The address and editing commands are separate, so \$pattern$s_foo_bar_ and /pattern/s#foo#bar# work as well.

like image 31
that other guy Avatar answered Dec 19 '22 18:12

that other guy


You absolutely can use alternate delimiters for the matching address (/regex/), but you need to tell sed that you intend to do matching with that delimiter. The way you do this is with a leading backslash \. So your command can be:

sed -e '\|somevar|s|foo|bar|' config.h

or just as easily:

sed -e '\%somevar%s|foo|bar|' config.h

Reference: POSIX reference for sed

like image 41
bonsaiviking Avatar answered Dec 19 '22 18:12

bonsaiviking


My vote is for feature.

They are two different commands, the searching one /.../ and the substitution one, s/.../.../. As far as I know it only lets change the separator of the substitution command.

In perl is similar. You can do a regular expression search with /.../ but if you want to change the separator you have to explicity mark it as a search with the m, like: m|...|.

I guess it will be related with some lexical parsing issue, but I don't know the reason, though.

like image 41
Birei Avatar answered Dec 19 '22 18:12

Birei


Feature. In

 sed -e '/somevar/s/foo/bar/' config.h

the /somevar/ is an address. Addresses must be distinguishable from other functions, like y (yank), i (insert), a (append) and many more. In general, sed commands are parsed as

 [address[,address]]function[arguments]
like image 34
Jens Avatar answered Dec 19 '22 19:12

Jens