How to replace current string line with another line in vim? - vim

I'd like to replace current string line with another (for example the another line is placed in 5 lines above current line). I can do it with a pair of commands
dd
:-5t-1
Is there the shorter way to obtain same goal?

dd
:-5t-1
is already pretty short if you ask me. But you can squeeze everything into a one-liner:
:d|-5t-1
and remove the 1 because it's implied by -:
:d|-5t-
Barring making a custom command or mapping I don't see how you could make it shorter.

:-5y<CR>Vp
is it shorter?
if you need do that really often, add this into your vimrc:
command! -range R d|<line1>,<line2>t-
then you can just do :-5R replace current line with -5 line
or 2,4R to cp line 2-4 (3 lines) to current line, and replace current line.

If you don't mind a plugin, my LineJuggler plugin offers a ]r command (and many more):
]r Fetch the line [count] visible lines above the current line and replace the current line with it.
With it, your example would be the short and easy 5]r
In addition, the companion LineJugglerCommands plugin now offers a similar :Replace Ex command. Again, your example would be
:Replace -5

Related

Vim: How to delete the same block of text over the whole file

I'm reviewing some logs with Java exception spam. The spam is getting is making it hard to see the other errors.
Is is possible in vim to select a block of text, using visual mode. Delete that block every place it occurs in the file.
If vim can't do it, I know silly question, vim can do everything. What other Unix tools might do it?
Sounds like you are looking for the :global command
:g/pattern/d
The :global command takes the form :g/{pat}/{cmd}. Read it as: run command, {cmd}, on every line matching pattern, {pat}.
You can even supply a range to the :delete (:d for short) command. examples:
:,+3d
:,/end_pattern/d
Put this togehter with the :global command and you can accomplish a bunch. e.g. :g/pat/,/end_pat/d
For more help see:
:h :g
:h :d
:h :range
Vim
To delete all matching lines:
:g/regex/d
To only delete the matches themselves:
:%s/regex//g
In either case, you can copy the visual selection to the command line by yanking it and then inserting it with <C-r>". For example, if your cursor (|) is positioned as follows:
hello wo|rld
Then you can select world with viw, yank the selection with y, and then :g/<C-r>"/d.
sed
To delete all matching lines:
$ sed '/regex/d' file
To only delete the matches themselves:
$ sed 's/regex//g' file
grep
To delete all matching lines:
$ grep -v 'regex' file
grep only operates line-wise, so it's not possible to only delete matches within lines.
you can try this in vim
:g/yourText/ d
Based on our discussion in the comments, I guess a "block" means several complete lines. If the first and last lines are distinctive, then the method you gave in the comments should work. (By "distinctive" I mean that there is no danger that these lines occur anywhere else in your log file.)
For simplifications, I would use "ay$ to yank the first line into register a and "by$ to yank the last line into register b instead of using Visual mode. (I was going to suggest "ayy and "byy, but that wold capture the newlines)
To be on the safe side, I would anchor the patterns: /^{text}$/ just in case the log file contains a line like "Note that {text} marks the start of the Java exception." On the command line, I would use <C-R>a and <C-R>b to paste in the contents of the two registers, as you suggested.
:g/^<C-R>a$/,/^<C-R>b$/d
What if the yanked text includes characters with special meaning for search patterns? To be on the really safe side, I would use the \V (very non-magic) modifier and escape any slashes and backslashes:
:g/\V\^<C-R>=escape(#a, '/\')<CR>\$/,/\V\^<C-R>=escape(#b, '/\')<CR>\$/d
Note that <C-R>= puts you on a fresh command line, and you return to the main one with <CR>.
It is too bad that \V was not available when matchit was written. It has to deal with text from the buffer in a search pattern, much like this.

vim Jump to the next non-identical line

I am looking at files that may have several consecutive identical lines.
Is there a easy way of jumping to the next non-identical line?
Alternatively I would like to be able to fold all the lines that are equal to the initial one showing just the number of linees that are folded.
You could define your own fold-expr:
first set fdm:
:set fdm=expr
then
:set foldexpr=getline(v:lnum)==#getline(v:lnum-1)?1:0
now you can test by typing zM, to close all fold, if you are lucky ^_^ all duplicated lines are folded.
you could type zR to open all folds.
if it works and you open those kind of file very often, you could put the above lines in your .vimrc.(au with ft) if only one time job, you can write mode line into that file.
Try this:
:nmap <F2> "1y$<CR>/^\(<C-R>1$\)\#!<CR>
It maps F2 to:
copy the current line into register 1
search for (and move to) the first line that does not match the contents of register 1
This seems to work well, unless the text of your copied line has escaped characters that will confuse the search regexp. This is because register 1 is just dropped into the search expression without escaping. This would be tricky to fix reliably, but for normal log files, it shouldn't be much of a problem.
Also: if you're not married to vim and just need to read the non-consecutively-duplicated lines of a file, the canonical UNIX way is:
uniq filename
If you want to be in vim but won't need to make changes to the file, try:
:%!uniq
(If you try the latter, be sure to exit without saving)

How to append every third line in Vim?

I'm not at all familiar with Vim but I'm working with large text files (~1G) and my standard text editors weren't cutting it.
My files are currently in this format:
Arbitrary_title_of_sequenceA
SEQ1SEQ1SEQ1SEQ1
SEQ2SEQ2SEQ2SEQ2
Arbitrary_title_of_sequenceB
SEQ1SEQ1SEQ1SEQ1
SEQ2SEQ2SEQ2SEQ2
I need a convenient way of appending the "SEQ2" line to the "SEQ1" line like so:
Arbitrary_title_of_sequenceA
SEQ1SEQ1SEQ1SEQ1SEQ2SEQ2SEQ2SEQ2
Arbitrary_title_of_sequenceB
SEQ1SEQ1SEQ1SEQ1SEQ2SEQ2SEQ2SEQ2
Considering the size of these files, doing each line separately isn't really an option. Any help would be much appreciated!
What about providing a correct sample to begin with?
:g/SEQ1/norm Jx
does what I think you want.
:g/SEQ1 is the :global command which allows you to act on each line containing the pattern SEQ1. See :help :global.
norm is the :normal command that you use to perform a normal mode command, here on every line matched by :g/SEQ1. See :help :normal.
After that comes the normal command in question:
J is used to join the current line with the line below.
x is used to remove the <Space> automatically added by Vim.
:1,$s/\(.*\n\)\(.*\)\n\(.*\n\)/\1\2\3/
1,$ -> range is all file
s/PAT1/PAT2/ -> substitute PAT1 with PAT2
.* -> match any character except new line
\n -> match new line
\(PAT1\) -> capture/remember the string that matched PAT1
\1,\2,\3 -> refers to the captured string for captures in order
Also using sed instead of vim should be faster:
sed -i 'n;N;s/\n/ /' input_file
This can be summarized as:
Read a line
Read another line and print previous line (n)
Read another line and append it to the previous line (N)
find the first newline and change it to space (s/\n/ /)
print the line (or merged lines)
I think romainl's solution is the best if you have a reliable "SEQ1" pattern you can grab onto. If not and you want to literally join every third line, you could easily do this with a macro:
qqjJxjq
Hit G to see how many lines are in the file and just repeat the macro that many times (it doesn't matter that it's higher than you need). So if the file was 1000 lines you could do 1000#q. This kind of solution is easy to remember and integrate into your normal workflow.

How to repeat the same search and replace command over disjunct line ranges in Vim?

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:
:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g
I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).
Short of writing a function, is there any neater way of doing this?
On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running
:'a,'b s/\=#s/\=#r/ | 'c,'d s/\=#s/\=#r/ | 'e,'f s/\=#s/\=#r/
This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=#r was fine, but the \=#s bit in the search pattern gave me an error.
Any tips would be appreciated.
If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).
The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.
Then use :g in conjunction with :s. eg:
:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g
Finally, remove the temporary prefixes.
In order to get rid of the necessity to retype the same search
pattern, substitution string and flags, one can simply use the
:& command with the & flag:
:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&
(See :help :& for details.)
Instead of using marker use this one :
:68,104s/FOO/BAR/g << substitue from line 68 to 104
This should make your job a little bit easier and clearer.
inspired by #Vdt's answer:
I am not sure but you could write all the substitutions down in a file and source that file i think.
substitutions.vim:
68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g
and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.
With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.
The s/\=#s/\=#r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=#s. Preferably use the default register, then it's a simple s//, but you probably know that already.
When performed over a closed fold, substitutions are limited to that fold.
fold each region
put the cursor on one closed fold
perform the substitution: :s/foo/bar<CR>
move to the next closed fold with zj or zk
use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
repeat…
You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.
Here's the simplest way to do it
:5,10s/old/new/g
5,10 : startlinenum,endlinenum

How to add line numbers to range of lines in Vim?

How can I add line numbers to a range of lines in a file opened in Vim? Not as in :set nu—this just displays line numbers—but actually have them be prepended to each line in the file?
With
:%s/^/\=line('.')/
EDIT: to sum up the comments.
This command can be tweaked as much as you want.
Let's say you want to add numbers in front of lines from a visual selection (V + move), and you want the numbering to start at 42.
:'<,'>s/^/\=(line('.')-line("'<")+42)/
If you want to add a string between the number and the old text from the line, just concatenate (with . in VimL) it to the number-expression:
:'<,'>s/^/\=(line('.')-line("'<")+42).' --> '/
If you need this to sort as text, you may want to zero pad the results, which can be done using printf for 0001, 0002 ... instead of 1, 2... eg:
:%s/^/\=printf('%04d', line('.'))/
Anyway, if you want more information, just open vim help: :h :s and follow the links (|subreplace-special|, ..., |submatch()|)
cat -n adds line numbers to its input. You can pipe the current file to cat -n and replace the current buffer with what it prints to stdout. Fortunately this convoluted solution is less than 10 characters in vim:
:%!cat -n
Or, if you want just a subselection, visually select the area, and type this:
:!cat -n
That will automatically put the visual selection markers in, and will look like this after you've typed it:
:'<,'>!cat -n
In order to erase the line numbers, I recommend using control-v, which will allow you to visually select a rectangle, you can then delete that rectangle with x.
On a GNU system: with the external nl binary:
:%!nl
With Unix-like environment, you can use cat or awk to generate a line number easily, because vim has a friendly interface with shell, so everything work in vim as well as it does in shell.
From Vim Tip28:
:%!cat -n
or
:%!awk '{print NR,$0}'
But, if you use vim in MS-DOS, of win9x, win2000, you loss these toolkit.
here is a very simple way to archive this only by vim:
fu! LineIt()
exe ":s/^/".line(".")."/"
endf
Or, a sequence composed with alphabet is as easy as above:
exe "s/^/".nr2char(line("."))."/"
You can also use a subst:
:g/^/exe ":s/^/".line(".")."^I/"
You can also only want to print the lines without adding them to the file:
"Sometimes it could be useful especially be editing large source files to print the line numbers out on paper.
To do so you can use the option :set printoptions=number:y to activate and :set printoptions=number:n to deactivate this feature.
If the line number should be printed always, place the line set printoptions=number:y in the vimrc."
First, you can remove the existing line numbers if you need to:
:%s/^[0-9]*//
Then, you can add line numbers. NR refers to the current line number starting at one, so you can do some math on it to get the numbering you want. The following command gives you four digit line numbers:
:%!awk '{print 1000+NR*10,$0}'
The "VisIncr" plugin is good for inserting columns of incrementing numbers in general (or letters, dates, roman numerals etc.). You can control the number format, padding, and so on. So insert a "1" in front of every line (via :s or :g or visual-block insert), highlight that column in visual-block mode, and run one of the commands from the plugin.
If someone wants to put a tab (or some spaces) after inserting the line numbers using the this excellent answer, here's a way. After going into the escape mode, do:
:%s/^/\=line('.').' '/
^ means beginning of a line and %s is the directive for substitution. So, we say that put a line number at the beginning of each line and add 4 spaces to it and then put whatever was the contents of the line before the substitution, and do this for all lines in the file.
This will automatically substitute it. Alternatively, if you want the command to ask for confirmation from you, then do:
:%s/^/\=line('.').' '/igc
P.S: power of vim :)
The best reply is done in a duplicate question.
In summary:
with CTRL-V then G I 0 You can insert a column of zero.
Then select the whole column and increment:
CTRL-V g CTRL-A
See also: https://vim.fandom.com/wiki/Making_a_list_of_numbers#Incrementing_selected_numbers

Resources