How do I yank all matching lines into one buffer? - vim

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. :)

Related

I need to replace the second occurrence of string1 by string2 on every line - vim

I know that there are multiple occurrences of string1 in every line of the file. I am looking for the shortest/quickest way to replace the second one by string2. Any method will do though vim is my preference.
You can find the second occurrence of "string1" using \zs.
Based on the :h \zs example, it will be
:%s/\(.\{-}\zsstring1\)\{2}/string2
It'll be more straightforward using external sed in Vim command mode
:%!sed 's/string1/string2/2'
See https://vi.stackexchange.com/questions/8621/substitute-second-occurence-on-line for more ways to accomplish it.
Here is one way:
:%normal 0/string1^Mncgnstring2<CR>
Breakdown:
:[range]normal <macro> executes normal mode <macro> on every line in [range],
% is a shorthand for range [<first line>,<last line>], which covers every line in the buffer,
0 is the first command of our macro, it places the cursor on the first column of the line, which is a good habit to have,
/string1^M moves the cursor to the first match for string1, the ^M is a literal <CR> obtained with <C-v><CR>,
n moves the cursor to to the next match,
cgnstring2 changes the current match to string2.
See :help :range, :help :normal, :help gn.
But it would have been more interesting to see what you tried and fix it, rather than provide you with a working solution.

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.

Exiting exe mode in a macro

I had a large file I was trying to reformat which involved removing the 2nd to nth repeating sets on 2 to 100 lines per duplicate.
The data looked like
element1.element2.element...field.comment
I wanted to remove the repetition in elements after the first instance so of course I went complicated :) and did a macro something like
In a macro Yanked first element on current line to register p and then processed lines yanking the first element into register o and then doing, still in the macro
:if (#p=!#o)|:.s/paste register p//g|else|:norm! j|endif
Now this worked OK except when it got to a line where #p<>#o the :norm! j part stayed in : mode until I manually escaped once or twice then executed the :norm! j command.
I solved the problem an easier way but would like to know why it was only on the else portion that it wouldn't leave :ex mode.
From :help norm
:norm[al][!] {commands} *:norm* *:normal*
...
This command cannot be followed by another command,
since any '|' is considered part of the command.
...
An alternative is to use |:execute|, which uses an
expression as argument. This allows the use of
printable characters to represent special characters.
Example: >
:exe "normal \<c-w>\<c-w>"
So this would do the trick:
:if (#p=!#o)|:.s/paste register p//g|else|:exe "norm j"|endif

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.

How can I search for and then copy the lines in vi/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.

Resources