I want to replace all instance of abc with 123 provided it isn't preceded by https://.


Initial="# Start";

Final="$" # Line Number
sed -r -e "/${Initial}/,${Final}s/${orig}/${new}/g" ${file} # Final doesn't accept $


# Start

The path is https://abc.md for abc.md
The path for abc.md is https://abc.md 

Expected Output:

# Start

The path is https://abc.md for 123.md
The path for 123.md is https://abc.md 

How can I achieve this?

Note: I also need to use ${Initial} and ${Final} to denote between which the pattern might exist.

This answer doesn't work for the case The path is: https://abc.md abc.md

  • Would you be happy with a solution that uses line numbers (not regular expressions for addressing the relevant bit of the input)? – Kusalananda Dec 13 '18 at 12:13
  • @Kusalananda No, because the line numbers are nof fixed. – Porcupine Dec 13 '18 at 12:15
  • Is the substitution always happening from the matching of some pattern and to the end of the file? – Kusalananda Dec 13 '18 at 12:18
  • @Kusalananda Yes the substitution happens between ${Iniital} and to end of the file. – Porcupine Dec 13 '18 at 12:19

What you are looking for is negative-look-behind, which neither sed or awk supports. I recommend going with perl, e.g.:

export Initial="# Start"
export orig="abc"
export new="123"
export Final="5"

perl -pe '
 $flag=1 if /$ENV{Initial}/;
 s,(?<!https://)$ENV{orig},$ENV{new},g if $flag;
 $flag=0 if $. == $ENV{Final};
' $file
  • Does not work with The path is https://abc.md for abc.md as per the OP's updated requirement. I updated my answer, and I think you can update yours ;-) – sudodus Dec 13 '18 at 18:41
  • @sudodus: It does if it occurs before the Final line, line number 5 in the above example – Thor Dec 13 '18 at 18:43
  • You are right about that. (A bit difficult to see for a perl illiterate, but it works) :-) – sudodus Dec 13 '18 at 18:47
  • @Thor How to make Final as the last line? In sed we use $. – Porcupine Dec 13 '18 at 23:11
  • 1
    @Nikhil: You can also set Final to 0, which will never be true – Thor Dec 14 '18 at 6:34

You could temporarily change all instances that start with https://to something else, not containing abc, and change them back when you've finished.

Lazy method

sed -e 's_https://abc_protected_g;/# Start/,$s_abc_123_g;s_protected_https://abc_'

You'll have to pick some value for the temporary string, that you can be certain won't appear in your input (and doesn't contain any special characters that would interfere with sed or shell quoting). I used protected but something more distinctive is advisable.

Thorough method

If you can't be certain that your temporary string won't occur in the input, a longer but safer alternative would be to use two replacement strings:

sed -e 's/X/Xv/g;s_https://abc_Xu_g;/^# Start/,$s/abc/123/g;s_Xu_https://abc_g;s/Xv/X/g'

The requirements of these two temporary strings are:

  • They have a common prefix (I used X, but it can be longer/more readable)
  • They don't occur within the other variables: orig, new, Initial or Final
  • As before, they don't contain characters that would break the sed expression
  • It is interesting to see PCRE features implemented in a pure BRE way, in a single sed invocation. I am wondering whether other PCRE features have their equivalents as well. – Weijun Zhou Dec 14 '18 at 2:10
  • @WeijunZhou As noted elsewhere this does not reimplement negative lookaheads, only the subproblem of negative lookaheads with a static string. Some Perl / PCRE extensions are purely convenience / syntactic sugar which is straightforward (but often cumbersome or boring) to replace with traditional regex, but e.g. lookarounds genuinely extend the formalism to something which is no longer theoretically equivalent (reducible) to regular expressions. – tripleee Dec 14 '18 at 6:13
  • There is no contradiction. A PCRE not being reducible to regular expression is one thing, and I agree on that. Using sed to modify the text to assist in pattern matching is another thing, which is no longer simple pattern patching and hence does not contradict. To put it more clearly, there is still no way to use grep to match lookaround patterns with BRE or ERE. – Weijun Zhou Dec 14 '18 at 7:40

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.