5

I'm learning how substitution works in Vim. And I find this example really weird to understand: :?a?,/b/s/bad/good. I know what this command will do in general. But why use two question marks to wrap a? What does it mean?

  • @MartinTournoij I could be wrong but isn't it a bit different? Here the ? are in the range section of the command and if I understand :h :range properly they reference to ?{pattern}[?] the previous line where {pattern} matches which is not the same thing as what I asked in the question you linked. – statox Jul 28 at 11:49
  • 2
    Oops sorry, you're right @statox; I was in a hurry and misread the question 😅 – Martin Tournoij Jul 28 at 13:08
6

The command is composed of two parts:

:?a?,/b/            The range 
        s/bad/good  The substitution command

You probably already know that the :s command can take a range:

  • :s/bad/good will do the substitution on the current line
  • :%s/bad/good will do the substitution on all the lines of the buffer
  • And /pattern1/,/pattern2/s/bad/good will perfom the substitution between the next line matching pattern1 and the next one matching pattern2.

The help :h :range gives us the different items we can use as a range:

Line numbers may be specified with:     *:range* *{address}*
    {number}    an absolute line number
    .       the current line              *:.*
    $       the last line in the file         *:$*
    %       equal to 1,$ (the entire file)        *:%*
    't      position of mark t (lowercase)        *:'*
    'T      position of mark T (uppercase); when the mark is in
            another file it cannot be used in a range
    /{pattern}[/]   the next line where {pattern} matches     *:/*
    ?{pattern}[?]   the previous line where {pattern} matches *:?*
    \/      the next line where the previously used search
            pattern matches
    \?      the previous line where the previously used search
            pattern matches
    \&      the next line where the previously used substitute
            pattern matches

Take a particular note of ?{pattern}[?] the previous line where {pattern} matches: this is what is used in your question.

Your command :?a?,/b/s/bad/good will be applied to the lines between the previous line matching a and the next one matching b. To make the difference clear between // and ?? here are two examples:

Take this file:

a
bad
b
bad
a

With you cursor on b in line 3 if you use :?^a$?,/^b$/s/bad/good you will get:

a
good
b
bad
a

Because line 1 is the previous one matching ?^a$? and line 3 is the next one matching /^b$/

Now if you use :/^a$/,/^b$/s/bad/good you will get

a
bad
b
good
a

Because the next line matching /^a$/ is line 5 and Vim know how to reverse the inverted ranges.


Note that in the range part of the command changing // to ?? changes how the action behave. This is different than changing the / characters in the substitution part of the command where s/bad/good/, s?bad?good? and s;bad;good are all equivalent as explained in this question.

| improve this answer | |

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.