How can I search for and then copy the lines in vi/vim? - vim

I guess it is better to explain with an example, so here it is:
My useful line.
Some useless line.
Some other useless line.
Another useful line - oh I want this!
This is useful, I want this too!
In this example, I am searching for the string "useful". So, I want to copy lines 1, 4 and 5 to clipboard. How can I do this with vim?

First clear register a (you can use any letter a-z) for using.
:let #a=''
Then run the magic.
:g/useful/yank A
It will search for lines matching pattern "useful" and then run command :yank A to them. Capital A will append to the register a.
If your vim is configured with global Windows/X clipboards, you can run
:let #+=#a
to copy the register a's content to the clipboard.

Related

Vim - sort the contents of a register before/after pasting it?

As part of a project of mine I'm trying to move certain lines from a file to the top, sorted in a certain fashion. I'm not sure how to do the sort once those lines are up there - I don't want to disturb the other lines in the file.
I'm moving them by yanking them and putting them back down, like so:
g:/pattern/yank A
g:/pattern/d
0put A
This moves all the lines I specify up to the top of the file like I need, but now I need to sort them according to a pattern, like so:
[range]sort r /pattern2/
Is there a way to sort the contents of a register before pasting it? Or a way to sort only lines which match /pattern/? (because all the yanked lines will, of course).
I'm stymied and help would be appreciated.
edit - a possible workaround might be to count the number of lines before they're yanked, and then use that to select and sort those lines once they're placed again. I'm not sure how to count those lines - I can print the number of lines that match a pattern with the command :%s/pattern//n but I can't do anything with that number, or use that in a function.
The whole point of :g/pattern/cmd is to execute cmd on every line matching pattern. cmd can, of course, be :sort.
In the same way you did:
:g/pattern/yank A
to append every line matching pattern to register a and:
:g/pattern/d
to cut every line matching pattern, you can do:
:g/pattern/sort r /pattern2/
to sort every line matching pattern on pattern2.
Your example is wasteful anyway. Instead of abusing registers with three commands you could simply do:
:g/pattern/m0
to move every line matching pattern to the top of the buffer before sorting them with:
:g//sort r /pattern2/
See :help :global, :help :sort, :help :move.
I know this is old, and may not be of any use to you anymore, but I just figured this one out today. It relies on the system's sort command (not vim's). Assuming you're saving to register A:
qaq
:g/pattern/yank A
<C-O>
:put=system('sort --stable --key=2,3',#A)
qaq: clears register A of anything
:g/pattern/yank A: searches current buffer for pattern and copies it to register A
<C-O>: pressing Ctrl+O in normal mode returns you to the last place your cursor was
:put=system('sort --stable --key=2,3',#A): sends the contents of register A to the sort command's STDIN and pastes the output to the current position of the cursor.
I mapped this whole thing to <F8>:
noremap <F8> qaq:g/pattern/yank A<CR><C-O>:put=system('sort --stable --key=2,3',#A)<CR>
I don't know how janky this is considered, cuz I'm a complete noob to vim. I spent hours today trying to figure this out. It works for me and I'm happy with it, hopefully it'll help someone else too.

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 do I yank all matching lines into one buffer?

How do you yank all matching lines into a buffer?
Given a file like:
match 1
skip
skip
match 2
match 3
skip
I want to be able issue a command to yank all lines that match a pattern (/^match/ for this example) into a single buffer so that I can put it into another doc, or into a summary or whatever.
The command should wind up with this in a buffer:
match 1
match 2
match 3
My first thought was to try:
:g/^match/y
But I just get the last match. This makes sense, because the :g command is effectively repeating the y for each matching line.
Perhaps there is a way to append a yank to buffer, rather than overwriting it. I couldn't find it.
:g/^match/yank A
This runs the global command to yank any line that matches ^match and put it in register a. Because a is uppercase, instead of just setting the register to the value, it will append to it. Since the global command run the command against all matching lines, as a result you will get all lines appended to each other.
What this means is that you probably want to reset the register to an empty string before starting: :let #a="" or qaq (i.e., recording an empty macro).
And naturally, you can use the same with any named register.
:help registers
:help quote_alpha
:help global
Using Vi/Vim: Ex and Ex-like Commands
:help registers
:help quote_alpha
Specify a capital letter as the register name in order to append to it, like :yank A.
Oh I just realized after commenting above that it's easy to yank matching lines into a temporary buffer...
:r !grep "pattern" file.txt
The simplest solutions come once you've given up on finding them. :)

Vim copy and paste

My previous question seems to be a bit ambiguous, I will rephrase it:
I have a file like this:
copythis abc
replacethis1 xyz
qwerty replacethis2
hasfshd replacethis3 fslfs
And so on...
NOTE: replacethis1, replacethis2, replacethis3, ... could be any words
How do I replace "replacethis1","replacethis2","replacethis3",.. word by "copythis" word by using minimum vim commands.
One way I can do is by these steps:
delete "replacethis1","replacethis2","replacethis3",.. by using 'dw'
copy "copythis" using 'yw'
move cursor to where "replacethis1" was and do 'p'; move cursor to where "replacethis2" was and do 'p' and so on...
Is there a better way to do this in VIM (using less number of vim commands)?
Since you changed your question, I'd do it this way:
Move to the first "replacethis1" and type cw (change word), then type "copythis" manually.
Move to the next "replacethis", hit . (repeat last operation)
Move to the next "replacethis", hit .,
and so on, and so on.
If "copythis" is a small word, I think this is the best solution.
The digit needs to be included, and there could be more than one instance per line:
:%s/replacethis\d/copythis/g
Given that "replacethis[1-3]" can be arbitrary unrelated words, the quickest/simplest way to do this globally would be:
:%s/replacethis1\|replacethis2\|replacethis3/copythis/g
(Note that you need to use \| to get the pipes to function as "or". Otherwise, vim will look for the literal | character.)
I've been struggling with this for a long time too, I think I just worked out the cleanest way:
Use whichever command is cleanest to put copythis into register r:
/copythis
"rye
Then go to the replacement and replace it with the contents of r:
/replacethis
cw<CTRL-R>r<ESC>
Then you can just n.n.n.n.n.n.n. for the rest of them, or if they're wildly different just go to the beginning of each and hit .
The key is replacing and pasting in one step so you can use . later.
:%s/copythis/replacethis/g
To replace all occurrences of copythis with replacethis. Or you can specify a range of line numbers like:
:8,10 s/copythis/replacethis/g
Note, the /g on the end will tell it to replace all occurrences. If you leave that off it will just do the first one.
create this mapping:
:map z cwcopythis^[
( ^[ is the escape character, you can type it in vim using Ctrl+V Ctrl+[ )
go to each word you want to replace and press z
if u need to do essentially the same action multiple times - swap 1st word of one line with second word of the next line, I say you could record a macro and call it whenever you need to
Have you tried string replacement?
%s/replacethis/copythis
A host of other parameters are possible to fine-tune the replacement. Dive into the Vim help for more details. Some more examples here.
You can remap e.g. the m key in normal mode to delete the word under the cursor and paste the buffer: :nnoremap m "_diwP.
Then you can just copy the desired word, move the cursor anywhere onto the to-be-replaced word and type m.
EDIT: Mapping to m is a bad idea since it is used to mark locations. But you can use e.g. ; anyway.

Resources