VIM substitution: Using the current line as the search string - vim

Assuming the following text file, which is actually a data dump of funds and price statistics:
PBCPDF
05/01/2006
0.0000
0.0000
PBCPDF
0.0000
06/01/2006
0.0000
0.0000
PBCPDF
0.0082
[… lines repeat …]
What I wanted to achieve is to delete all instances of PBCPDF except for the first one, which I could write the substitution command as :.+1,$s/PBCPDF\n//g.
However, since I wanted to program a macro to process multiple fund names, I need a means to use some sort of pattern that would retrieve the current line as the search pattern without me doing it by hand.
Is there a way to do that?

ggyy:+1,$s/<Ctrl-R>"<BS>//g
Let's see what that does, exactly.
Go to the first line
gg
Yank the entire liine
yy
Start the substitution
:+1,$s/
Get the current line from the register to which you yanked it to:
<Ctrl-R>"
Note: Don't type the ", you have to actually hold Control and press R, followed by a double quote.
Delete the end of line character:
<BS>
Note: press Backspace.
Replace with nothing.
//g
You can record this in a macro by wrapping it with a qq/q, or whatever.

If you're doing this in a macro (using the q command) it's smart enough to catch when you do some of the more esoteric keystroke commands - the one I'm specifically thinking about is CTRL-R, CTRL-W, which will insert the word under the cursor into the command line. So, for example, if you did
ESC
qw
/[CTRL-R, CTRL-W] (you should see the word under the cursor enter the command line)
[enter]
q
then you just created a macro that searches for the next word in the buffer that's the same as this one, get it? It will dynamically yank the word under the cursor into the command as part of the macro. There might be something like CTRL-W in the command above that does this for the whole line, but I don't know it offhand.

Having studied sykora's and Matt's answers, I came up with a slightly better improvement which works on a line-wise basis rather than a word-wise basis (as per Matt's solution).
This command is to be executed after the cursor is positioned on the line of which the deletion is to occur:
:.+1,$s/<CTRL-R>=getline(".")<CR>\n//g
Explanation for VIM beginners:
:.+1,$s/
Start substitution from the line after the current line (.+1) and the end-of-file ($)
<CTRL-R>=getline(".")<CR>\n
<CTRL-R> refers to the actual pressing of the CTRL and R keys, which allows you to enter special expressions. In this case, =getline(".") which retrieves the text of the current line.
<CR> refers to the pressing of the enter key, which completes the <CTRL-R> expression. I also specify the newline character (\n) at the end of the search pattern as I need to remove it.
//g
And then replace with nothing.

Related

How to select the similar text in gvim and modify them?

How I can select all the text start with foo_list starting from line 4 (see. below code) and rename them with list_values or any other preferred name ? Please note, I don't want to change in the first line.
Thanks in advance !
foo_list = [5, 2, 3, 1, 4]
def reverse_list_1():
foo_list=[0,10,20,40]
for i in reversed(foo_list):
print i,
foo_list=[0,10,20,40]
print foo_list[::-1]
for i in reversed(foo_list):
print i,
length = len(foo_list)
for i in range(length):
print foo_list[length-i-1],
Preferable solution: key map in the .vimrc or .gvimrc file, don't want to use any plugin.
That's a job for :substitute. You can specify the range with explicit line numbers (here: 4 to end of buffer $, or maybe next empty line /^$/):
:4,$substitute/\<foo_list\>/list_values/g
You can also first move to the first line and use the .,$ range.
Since that's still a lot of typing, you can pull in the current word (assuming you first position the cursor on the foo_list occurrence in line 4) into the command line via <C-R><C-W>.
Or, for a plugin solution, my ChangeGlobally plugin provides a mapping that avoids the use of :s.
:%s/foo_list/list_values/gc
This command says to replace the word foo_list with list_values in the whole document, asking for confirmation each time. Then for the first occurrence of foo_list on line 1, press n (to indicate NO) , and press y (to indicate YES) for all further occurrences to replace them. This solution works when you have to replace a few words. You can read the command as follows:
In the whole document (%), substitute (s) the word foo_list with list_values and do this globally (g), asking for confirmation (c) each time. For more options in the substitute command type :help :s in vim.
Solution 2 :
When there are thousands of words to replace, you surely don't want to type a y/n confirmation each time (which is enabled by the c flag in the end in the above command).
Take your cursor to line 4 and run
:.,$s/foo_list/list_values/g
Read the above command as from here (.) to the end of file (,$)
replace (s) the word foo_list with list_values
globally (g).
For small changes like this I like to use the gn motion. The gn motion visually selects the current search pattern. This makes for a powerful search/replace method when combining the gn motion with the change, c, operator and the repeat command, ..
Basic steps:
Make foo_list your search pattern. e.g. /foo_list or via *
Use c and gn to change the first foo_list. e.g. cgnbar_list<esc>
Now repeat that change on the next search result via .
Use n to advance to the next search results. (Hit n twice to skip an occurrence)
Keep using n and . until done
There is a nice Vimcasts episode on this topic: Operating on search matches using gn
For more information see:
:h gn
:substitute is the 'correct' way, but if you're only making a few changes, and you are not very experienced with ex commands, sometimes it takes longer to think through the command than to bounce through the list of changes you want to make using motions.
If you start with your cursor on the first instance of foo_list, hit '*' to jump to the next occurrence in the file. Hit 'ce' to delete to the end of the word and enter insert mode. Type in your new variable name and return to normal mode. Now you can jump through the rest of the file using 'n' to jump to the next occurrence (or 'N' to go back), and '.' to repeat your last edit action.

move cursor to next line after input from filter command in vim

In vim I filter, say the current single line, using !! through a Unix command. To achieve this I defined the following shortcut in .vimrc
:map <Enter> !!mycommand<CR>:,+1<CR>
Pressing <Enter> this takes me to the line below the current if mycommand replaces my single input line be exactly one output line. If the output has more lines (number of lines unknown before command execution) it will still take me to the line below the current.
Now, I would like to know how I can always get to the first line below the inserted output of mycommand.
The modified shortcut would then allow me to 'execute' the text file line by line using just <Enter> displaying the output each time.
If there is no way to do this without any previous knowledge of the output of mycommand, maybe there is one knowing say the first character of each output line.
Thanks a lot!
You can use the special marks '[ and '], which mark the start and end of the last changed (or yanked) text. Change your map to:
:map <Enter> !!mycommand<CR>']+
Note that I'm using + in place of your ex command. This will jump to the first non-blank character in next line. If that's not what you want, you may try simply j or, use a shorter version of your original map:
:map <Enter> !!mycommand<CR>']:+1<CR>
You don't really need the comma, to make this a range. This command is just a simplified :#, where # is a line number to jump. Here you can use . meaning "current line", and then :.+1 moves to the next line. But you can omit the dot, and that's why :+1 does the same.

Prepending a character followed by the line number to every line

I'm hand-editing CNC Gcode text files and need a way to reference locations in the file and on the toolpath.
I want to modify every line in the text file so that it begins with the the upper case letter N followed by the line number, incremented in tens for each successive line, then a whitespace followed by the original text on that line. How can I do this in Vim?
I'm not sure about vi, but (since you're using the vim tag) Vim allows you to accomplish your task as follows:
Adjust the first line by hand (insert a N10 at the beginning of the line), then put the cursor at the beginning of the next line.
Press qb to start recording a macro (the b names the register used to store the macro; feel free to use a different letter -- and definitely do use a different letter if you've got something useful stashed away in b).
Move the cursor upward to the beginning of the previous line (which you have adjusted by hand). Press v to start visual selection mode, then f to move the cursor to the next space on the line (if you use a single space as your whitespace separator, that is; adjust this step if you're using a tab or multiple spaces).
Press y to yank the selected text. This will also remove the visual selection.
Move the cursor to the beginning of the next line. Press P to insert the previously yanked text before the cursor, that is, on the very beginning of the line.
Move the cursor to the numeric part of the line header. Press 10 C-a (1, 0, control + A) to increment that number by 10.
Move the cursor to the beginning of the next line. Press q to stop recording the macro.
Press 10000000 #b to execute the macro 10000000 times or until it hits the end of the file. This should be enough to take care of all the lines in your file, unless it is really huge, in which case use a bigger number.
...or use Vim to write a simple script to do the job in whichever language you like best, then run it from a terminal (or from withing Vim with something like :!./your-script-name). ;-)
The following command will prepend ‘N<line number * 10>’ to every line:
:g/^/exe 'normal! 0iN' . (line('.')*10) . ' '
You can do it easily in Vim with this:
:%s/^/\=line(".")*10 . " "/
This replaces the start of every line with the result of an expression that gives the line number times ten, followed by a space.
I have not timed it, but I suspect it might be noticeably faster than the other Vim solutions.
Cheating answer:
:%!awk '{print "N" NR "0", $0}'
There are two ways to implement that without resorting to external
tools: via a macro or by using Vimscript. In my opinion, the first way
is a little cumbersome (and probably not as effective as the solution
listed below).
The second way can be implemented like this (put the code into your
.vimrc or source it some other way):
function! NumberLines(format) range
let lfmt = (empty(a:format) ? 'N%04d' : a:format[0]) . ' %s'
for lnum in range(a:firstline, a:lastline)
call setline(lnum, printf(lfmt, lnum, getline(lnum)))
endfor
endfunction
The NumberLines function enumerates all lines of the file in a given
range and prepends to each line its number according to the provided
printf-format (N%04d, by default).
To simplify the usage of this function, it is convenient to create
a command that accepting a range of lines to process (the whole file,
by default) and a optional argument for the line number format:
command! -range=% -nargs=? NumberLines <line1>,<line2>call NumberLines([<f-args>])

How can I insert text in the middle of the line to multiple lines in Vim?

Say I have ten lines and I want to prepend text to some word that occurs in those lines? It does not have to be at the beginning of the line.
From:
sdfsd foo sdfsd
sfsd foo fsdf
sdfsdf foo sdfsdf
to:
sdfsd bar(foo sdfsd
sfsd bar(foo fsdf
sdfsdf bar(foo sdfsdf
Is it also possible to not only prepend the bar( but actually surround foo with bar(foo)?
I would also like a quick way to append // comments to multiple lines (C-style comments).
I use Vim/GVim 7.2.
Go to the first foo, press Ctrl-v to enter visual block mode and press down until all the lines with foo are marked. Then press Shift-i to insert at the beginning (of the block). When you are finished and press Esc, the inserted characters will be added to each line at the left of the marked block.
To insert at the end, press again Ctrl-v, move up/down to mark all affected lines and then press End or $ to extend the selection until the end of the lines. Now you can press Shift-a to append at the end of all the lines, just like previously with Shift-i.
The visual selection can also be done with normal movement commands. So to comment a whole block in C you could move to the opening brace and type Ctrl-v % Shift-i // Esc.
To answer your first question, the below
:%s/foo/bar(&)/g
will look for foo, and surround the matched pattern with bar(). The /g will do this multiple times in one line.
Since you're just matching foo, you could do a simple :s/foo/bar(foo)/g. The above will work, however, if you decide to match on a regular expression rather than a simple word (e.g. f[a-z][a-z]). The '&' in the above represents what you've matched.
To prefix a set of lines I use one of two different approaches:
One approach is the block select (mentioned by sth). In general, you can select a rectangular region with ctrl-V followed by cursor-movement. Once you've highlighted a rectangle, pressing shift-I will insert characters on the left side of the rectangle, or shift-A will append them on the right side of the rectangle. So you can use this technique to make a rectangle that includes the left-most column of the lines you want to prefix, hit shift-I, type the prefix, and then hit escape.
The other approach is to use a substitution (as mentioned by Brian Agnew). Brian's substitution will affect the entire file (the % in the command means "all lines"). To affect just a few lines the easiest approach is to hit shift-V (which enables visual-line mode) while on the first/last line, and then move to the last/first line. Then type:
:s/^/YOUR PREFIX/
The ^ is a regex (in this case, the beginning of the line). By typing this in visual line mode you'll see '<,'> inserted before the s automatically. This means the range of the substitution will be the visual selection.
Extra tip: if your prefix contains slashes, you can either escape them with backslash, or you can use a different punctuation character as the separator in the command. For example, to add C++ line comments, I usually write:
:s:^:// :
For adding a suffix the substitution approach is generally easier unless all of your lines are exactly the same length. Just use $ for the pattern instead of ^ and your string will be appended instead of pre-pended.
If you want to add a prefix and a suffix simultaneously, you can do something like this:
:s/.*/PREFIX & SUFFIX/
The .* matches the whole line. The & in the replacement puts the matched text (the whole line) back, but now it'll have your prefix and suffix added.
BTW: when commenting out code you'll probably want to uncomment it later. You can use visual-block (ctrl-V) to select the slashes and then hit d to delete them, or you can use a substitution (probably with a visual line selection, made with shift-V) to remove the leading slashes like this:
:s:// ::
:normal to the rescue!
:%norm Wibar(
:%norm WEa)
:norm(al) replays the commands as if you had typed them:
W - goes to the next word
i - starts insertion mode
bar( - types the sequence 'bar('
Or in one line:
:%norm Wibar(ctrlvESCEa)
If you're running Windows then type ctrlq instead of ctrlv.
Yet another possibility (probably not-so-useful in your test case, but handy in other situations) is to cordon off the area you want to change with marks.
Put the cursor anywhere in the top line and press 'a
Put the cursor anywhere in the last line and press 'b
Issue the command :'a,'b s/foo/bar(&)/
I usually like visual block mode if everything is visible on the screen, and I usually prefer marks if the start and stop are separated by many screens.
Another simple regular expression is:
%s/^/<text you want to prepend>/
For the C-style comments, use the regexp answer by Brian, and match on line ending $, and insert away.

Delete first word of each line

How do I delete first word of each line in Vim?
How about a pattern on each line?
:normal to the rescue:
:%norm dw
It basically replays the arguments as if you were typing them in normal ('non-edit') mode.
From :help :
:norm[al][!] {commands}
Execute Normal mode commands {commands}.
This makes it possible to execute Normal mode commands typed on the
command-line. {commands} is executed like it is typed.
Going for cryptic here, in true vi style:
1Gq10dwjq100000#1
Randy fixed this up in the comments to work on more than 100000 lines:
ggqqdwj#qq#q
For those starting out with Vim, this breaks down to:
gg - Go to first line
qq - Record a macro into register 'q'
dwj#q - The macro:
dw - delete word at cursor
j - go down one line
#q - run the macro in register 'q'
q - Stop recording the macro
#q - Execute the macro in register 'q'
In essence, the macro is recursive - it deletes a word, moves down a line, then calls itself again, repeating for each line until end of file. The final '#q' is the initial (manual) call needed to set the macro off on every line.
I would use something like the following:
:%s/^\w+\s+//
The regular expression will match one or more "word" characters starting at the beginning of the line followed by at least one whitespace character. It will remove the word and any following whitespace. If a line can contain only a single word -- and you still want it removed -- you could use alternation to match either whitespace or the end of line.
:%s/^\w+(\s+|$)//
First word (where word is defined as no whitespace)
:%s/^\s*[^ ]* //g
Delete pattern:
:%s/< insert pattern here >//g
What about this?
:%!cut -s -d' ' -f2-
:%s,^[^ ]*,,
From the beginning of the line match anything, but not a space and replace with none.
Although this is an old question, if someone else is looking to do this you could do use visual block.
press ctrl+v
select all the lines you would want to edit
now use arrow keys to select the entire word
press 'x'
That would delete the first word in all the lines. This method is especially very handy to edit log files

Resources