问题
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?
回答1:
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.
回答2:
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.
回答3:
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
回答4:
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.
回答5:
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]
来源:https://stackoverflow.com/questions/20808095/why-do-alternate-delimiters-not-work-with-sed-e-pattern-s-a-b