1

I'd like to remove the period between the 6th comma and the 9th comma for the multiple lines of the following texts.

Change from:

4,Afghanistan,2,Medium,1951,1951.5,4134.756,3705.395,7840.151,12.009
4,Afghanistan,5,Constant fertility,2023,2023.5,21628.67,20554.282,42182.952,64.613

To:

4,Afghanistan,2,Medium,1951,1951.5,4134756,3705395,7840151,12.009
4,Afghanistan,5,Constant fertility,2023,2023.5,2162867,20554282,42182952,64.613

I have tried below two commands and it works well.

:normal 6f,v3f, 
:s/\%V.\+\%V/\=substitute(submatch(0),'\.','','g')/

But it doesn't work after I combine above two commands into one command as below:
:normal 6f,v3f,:s/\%V.\+\%V/\=substitute(submatch(0),'\.','','g')

I must have some misunderstandings about how to combine multi commands.
Very appreciate if anyone could point out my mistake and how to fix it.

2
1

The problem with your :normal command

You can't run ex commands from within a :normal command. Instead, you'll need to run your existing :normal command and then run the :substitute.

Try this:

:execute 'normal 6f,v3f,' | s/\%V.\+\%V/\=substitute(submatch(0),'\.','','g')

You need the :execute because otherwise there's no other way to complete the normal command so that you can run something else afterwards.

Running it on multiple lines

However, you can't apply this command to multiple lines with a :global command, because the :global will apply only to the first command before the bar. You will make a visual selection on every line, and then afterwards run your substitution once only.

The easiest fix for your actual task is probably to dispense with visual mode entirely, and instead use a single substitution that accounts for the commas in the regular expression itself:

:g/\./s/\v([^,]*,){6}\zs([^,]*,){3}/\=substitute(submatch(0),'\.','','g')/

The regular expression broken down:

\v([^,]*,){6}\zs([^,]*,){3}
\v                          " Using very-magic (so we don't need so many backslashes)
          {6}               " search for 6 copies
  (      )                  " of a group containing
       *                    " any number of
   [^,]                     " characters that aren't a comma
        ,                   " followed by a comma
             \zs            " start the match (and thus the replacement) here
                        {3} " search for 3 copies
                (      )    " of a group containing
                     *      " any number of
                 [^,]       " characters that aren't a comma
                      ,     " followed by a comma

An alternative solution using macros

An alternative solution is to use a macro instead of :normal

Try typing this:

qq6f,v3f,:s/\%V.\+\%V/\=substitute(submatch(0),'\.','','g')/<CR>q

...in which <CR> is a single press of your Enter key.

This records a macro into your "q register which actually does your f and v commands in normal/visual mode instead of using :normal to emulate this, and then performs the substitution.

You can then play this back on a single line by typing @q, or play it back on multiple lines with e.g.:

:g/\./norm! @q
4
  • Thanks for the solution, it works well for single line replacement. But when I apply below command to filter the two lines mentioned in above question global/\./execute 'normal! 06f,v3f,' | s/\%V.\+\%V/\=substitute(submatch(0),'\.','','g') Only the first line's period is removed, the second line is not affected. – macyou Feb 3 at 2:30
  • @macyou See my edit for a solution that can be applied to multiple lines at once – Rich Feb 3 at 11:48
  • very appreciate for the detailed explanation for different solutions. Both solutions works perfectly! And it corrects many of my misunderstandings and solve my long term problem about how to apply multi-commands to a range of lines. What a pity that vim doesn't support grammar like g/pattern/{cmd1|cmd2|...|cmd3}. I see some comments that vim can almost do anything that can be done by awk/sed. Do you think learning vim is enough for text processing tasks as a freshman? – macyou Feb 3 at 12:51
  • It depends what sort of text processing tasks you need to do! (You don't mention what you're a freshman in). I'm no expert, but my feeling is that while you can always hack something up in Vim, awk is better for doing the sort of things it's designed to do (this example probably being one of them). In my personal experience, there's less need to learn sed if you already know Vim well, but it does fit into a pipeline more naturally than Vim tends to. – Rich Feb 3 at 15:01
0

Well, the command separator is | (or <Bar>, esp. in mappings).

In this case, though, Rich is right: you need some more sophistication. I would write

:global/\./execute 'normal! 06f,v3f,' | substitute/\%V.\+\%V/\=tr(submatch(0), '.', ''))

where :normal! 0 is a bit more robust, and :global/\./ runs this only on lines which already contain a period (but on all such lines). I also used tr for this simple swap.

1
  • Thanks for the detailed explanation! I tried this command you recommended with "tr", it looks like it doesn't work as expected. :( – macyou Feb 3 at 2:34

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.