tags:

views:

472

answers:

6

How can I replace the first ten characters of a line where those ten characters match a particular pattern with the first ten characters from the line above?

Edit: It wasn't clear if I was asking to replace the first ten characters where the match could appear anywhere within the line, so maybe make a note in your answer if it deals with this case (call this case B and the intended one case A?)

A: 

You could use search and replace:

:7,9 s/foo/bar/c

This example searches from line 7 to 9 for each occurrence of 'foo', and replaces it with 'bar', asking for a confirm on each hit. If you don't want to confirm, drop the c at the end. Pick the range as you see fit and this should get you where you want

Sakkle
+4  A: 

Prehaps:

:%s/^\(.\{10}\)\(.*\n\)abcdefghij\(.*\)/\1\2\1\3/

Where 'abcdefghij' is the 10 character string on the 2nd line

gacrux
Hm... interesting...
Sakkle
What if the pattern that you want to match is not the first 10 characters of that line? This may not work in that case.
B Johnson
@B Johnson the question states that the 10 characters are in the beginning of the line.
Renato Besen
Nice. I didn't know that vim regexes could span lines.
rampion
@Renato: depends how you parse the sentence. Could be either: "the first 10 characters of (a line matching a particular pattern)" or "(the first 10 characters of a line) matching a particular pattern"
rampion
@rampion: A "line" is merely a presentational concept. To a regex it's all the same - a sequence of characters, some of which being newline characters. Sure, there are "^" and "$" anchors, and "." does stop at newline characters, but these are convenience defaults rather than actual limitations.
Tomalak
+2  A: 

If I have a complex action like that I usually record a macro using the q command. Something like (untested):

/<pattern>
qq
10x
k
10yl
j
P
n
q

And then repeatedy issue that macro as @q optionally prefixed with a count.

Peter van der Heijden
+1  A: 

Using only vim's motions and yanking/pasting.. Given the file contents of..

1234567890abcdef
qwertyuiopasdfgh

With the cursor on q, 10x, file becomes:

1234567890abcdef
asdfgh

Move the cursor to the first line (using k will do it), then do 10yl (yank 10 characters, right)

Then move back down one line, j, and paste P (upper case, to paste under cursor) and the file becomes:

1234567890abcdef
1234567890asdfgh

In short, starting with the cursor on q:

10xk10yljP

..which you could paste in, or assign to a macro

It would be shorter if there was an obvious shortcut to paste by overwriting, but I couldn't find such a thing

One other option is an incredibly obscure looking regex search/replace..

Visual-line select the two target lines, and run the following search-and-replace:

:'<,'>s/\(\(.\{10\}\).*\)\n\(.\{10\}\)\(.*\)$/\1\r\2\4/

Basically it grabs..

  • \1 - the entire first line
  • \2 - the first 10 characters (in a nested group)
  • a linebreak
  • \3 - the first ten characters of line two
  • \4 - the rest of the second line

Then it constructs the two lines as \1\n\2\4 - complete first line, linebreak, first 10 characters of first, remainder of second

dbr
+4  A: 

Something like this would work:

%s/^.\{10\}/\=strpart(get(getbufline("", line(".")-1), 0, ""), 0, 10)/

where ^.\{10\} is your actual pattern.

%s/                      # substitute all lines matching…
^.\{10\}                 # your pattern
/                        # …with…
\=                       # an expression:
strpart(                 # gets the part of a string
  get(                   # gets an element of a list
    getbufline(          # gets a list of lines from the current buffer
      "", line(".")-1)   # getbufline() the line before the current line
  , 0, "")               # get() first line in buffer, default to ""
, 0, 10)                 # strpart() first ten characters
/                        # …end of substitution
Tomalak
+3  A: 
:2,$g/<pattern>/s/^.\{10}/\=strpart(getline(line(".")-1),0,10)
  • 2,$ is our range (as the first line has no previous line)
  • g// lets you run a command on lines that match a given pattern.
  • s/^.\{10}/ will replace the first 10 characters of a line
  • \= lets you substitute the result of a vim expression in an :s//
  • line(".") is the current line number
  • getline(line(".")-1) is the text of the previous line
  • strpart(getline(line(".")-1),0,10) is the first 10 characters of the previous line

For example 2,$g/frog/s/^.\{10}/\=strpart(getline(line(".")-1),0,10) will change:

I like eating mangos
before frying frogs legs
I wish I had a puppy
She gave Dad a frog

To this:

I like eating mangos
I like eating frogs legs
I wish I had a puppy
I wish I had a frog
rampion
+1 for the \= trick, very nice
Peter van der Heijden