Jason Basanese
Jason Basanese

Reputation: 710

Is there an alternative to negative look ahead in sed

In sed I would like to be able to match /js/ but not /js/m I cannot do /js/[^m] because that would match /js/ plus whatever character comes after. Negative look ahead does not work in sed. Or I would have done /js/(?!m) and called it a day. Is there a way to achieve this with sed that would work for most similar situations where you want a section of text that does not end in another section of text?

Is there a better tool for what I am trying to do than sed? Possibly one that allows look ahead. awk seems a bit too much with its own language.

Upvotes: 0

Views: 713

Answers (1)

Ed Morton
Ed Morton

Reputation: 203229

Well you could just do this:

$ echo 'I would like to be able to match /js/ but not /js/m' |
    sed 's:@:@A:g; s:/js/m:@B:g; s:/js/:<&>:g; s:@B:/js/m:g; s:@A:@:g'
I would like to be able to match </js/> but not /js/m

You didn't say what you wanted to do with /js/ when you found it so I just put <> around it. That will work on all UNIX systems, unlike a perl solution since perl isn't guaranteed to be available and you're not guaranteed to be allowed to install it.

The approach I use above is a common idiom in sed, awk, etc. to create strings that can't be present in the input. It doesn't matter what character you use for @ as long as it's not present in the string or regexp you're really interested in, which in the above is /js/. s/@/@A/g ensures that every occurrence of @ in the input is followed by A. So now when I do s/foobar/@B/g I have replaced every occurrence of foobar with @B and I KNOW that every @B represents foobar because all other @s are followed by A. So now I can do s/foo/whatever/ without tripping over foo appearing within foobar. Then I just unwind the initial substitutions with s/@B/foobar/g; s/@A/@/g.

In this case though since you aren't using multi-line hold-spaces you can do it more simply with:

sed 's:/js/m:\n:g; s:/js/:<&>:g; s:\n:/js/m:g'

since there can't be newlines in a newline-separated string. The above will only work in seds that support use of \n to represent a newline (e.g. GNU sed) but for portability to all seds it should be:

sed 's:/js/m:\
:g; s:/js/:<&>:g; s:\
:/js/m:g'

Upvotes: 3

Related Questions