2

I have several lines, and each line has 2 quoted string, and I want to replace all space with "_" for the first quoted string.

How to do that? Could I use macro?

select 'a b c','a b c' union all
select 'a e1 c d','a e1 c d' union all
select 'a f11 c','a f11 c' union all
select 'a g1 c','a g1 c' union

result:

select 'a_b_c','a b c' union all
select 'a_e1_c_d','a e1 c d' union all
select 'a_f11_c','a f11 c' union all
select 'a_g1_c','a g1 c' union
Vivian De Smedt
  • 16,336
  • 3
  • 18
  • 37
Daniel Wu
  • 123
  • 4

2 Answers2

6

A macro like @Nobe4's may be the easiest, but you could also do a substitution. It can be tricky to express the substitution since you want it to be 'global' within the quoted string, but not really global for the line (i.e., replace all whitespace, but only for the first quoted string on the line). One way is to do a non-global substitution to replace the whole first quoted string, and for replacement use an expression which makes a global substitution on that string from ' ' to '_'.

:%s/'[^']*'/\=substitute(submatch(0), ' ', '_', 'g')/

or

:%s/'.\{-}'/\=substitute(submatch(0), ' ', '_', 'g')/

Maybe this feels a little clunky compared to a macro, but it can be a useful technique sometimes.

Details

  • : - enter cmdline mode
  • % - for all lines in buffer
  • s - substitute (the three / characters delimit the substitute command, see :help :substitute)
  • '.\{-}' or '[^']*' - regex that matches anything surrounded by single quotes
  • \= - substitute with an expression (see :help sub-replace-expression)
  • substitute() - the expression is a call to :help substitute()
  • submatch(0) - the entire matched string is retrieved with submatch(0) and is the first argument to substitute()
  • ' ', '_' - the substitution on the string
  • 'g' - global flag, i.e., substitute all occurrences

If we had ended the :substitute command with a global flag (/g) then it would have substituted all occurrences of quoted strings on the line. As it is, it only does the first. In the call to substitute() however we do use the global flag ('g') in order to replace all occurrences of ' ' in the quoted string.

jjaderberg
  • 3,489
  • 1
  • 14
  • 19
  • I like your solution, can you give some more details for the differents parts ? – nobe4 Aug 21 '15 at 10:27
  • It's always good to see examples of advanced search replace usage like this to try to boost one's knowledge. – NeilG Sep 25 '23 at 23:29
5

I think the macro is a good use here :

0vi':s/\%V /_/g^M

Decomposing the macro :

0        " go to the start of the line
vi'      " visually select inside '
:s/       " start a substitute command
\%V      " tell the substitute to operate over the last visual selection
 /_/g    " there is a ' ' here : replace every ' ' with a '_'
^M       " press <CR>

Reference for \%V : http://vim.wikia.com/wiki/Search_and_replace_in_a_visual_selection

You can then go line over line, or execute the macro over all lines at the same time (see https://stackoverflow.com/a/3338480/2558252)

nobe4
  • 16,033
  • 4
  • 48
  • 81
  • 1
    Do you really need to leave visual mode first? I know there are cases when you do, but I think with \%V you'd be fine starting the substitute from visual mode. Save some keystrokes :) – jjaderberg Aug 21 '15 at 10:26
  • 1
    0f'v; could be 0vi' – Kent Aug 21 '15 at 11:14
  • I think there is an <Esc> missing after the vi', otherwise you have to manually delete the :'<,'>s/ and retype it. At least in vim 8.0. – naught101 Jan 13 '19 at 22:30