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?

2
  • @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, 2020 at 11:49
  • 2
    Oops sorry, you're right @statox; I was in a hurry and misread the question 😅 Jul 28, 2020 at 13:08

1 Answer 1

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.

This site is temporarily in read-only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .