I need to replace the second occurrence of string1 by string2 on every line - vim - 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.

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.

swapping characters in ex

I am pretty new to vim and ex and I was wondering if anyone could help me with an area I am fuzzy on. I would like to know how to swap characters on every line or occurrence of a pattern. For example How would I swap the first 2 characters of every line in a file. I know it can be done and I'm pretty sure it involves the use of parentheses to store the chars. But thats is all I know. Also, Say I wanted to replace the 2nd char on everyline with some string, how would I do that?
To replace second character in each line to r in vim: :%s/^\(.\)./\1r/:
:%s/p/r/ replace pattern p with r for all lines (because of %);
^ start line;
\( start a group;
. any character (the first in this example);
\) end the group;
. any character (the second in this example);
\1 back reference to the first group (the first character in this example);
r replacement text.
To swap two first characters: :%s/^\(.\)\(.\)/\2\1/.
Swapping the first two characters on every line:
:%s/^\(.\)\(.\)/\2\1/g
Replacing the second character on every line with "string":
:%s/^\(.\)\(.\)/\1string/g
More info on the substitute command: http://vim.wikia.com/wiki/Search_and_replace
You can do the following to swap the two first chars of every line in the buffer:
:%norm xp
or:
:%s/\v^(.)(.)/\2\1
You'll need the :global command to apply the commands above on every line matching a specific pattern:
:g/foo/norm xp
or:
:g/foo/s/\v^(.)(.)/\2\1
Reference:
:help :normal
:help :global
:help :s
:help range

Vim: delete empty lines around cursor

Suppose I'm editing the following document (* = cursor):
Lions
Tigers
Kittens
Puppies
*
Humans
What sequence can I use to delete the surrounding white space so that I'm left with:
Lions
Tigers
Kittens
Puppies
*
Humans
Note: I'm looking for an answer that handles any number of empty lines, not just this exact case.
EDIT 1: Line numbers are unknown and I only want to effect the span my cursor is in.
EDIT 2: Edited example to show I need to preserve leading whitespace on edges
Thanks
Easy. In normal mode, dipO<Esc> should do it.
Explanation:
dip on a blank line deletes it and all adjacent blank lines.
O<Esc> opens a new empty line, then goes back to normal mode.
Even more concise, cip<Esc> would roll these two steps into one, as suggested by #Lorkenpeist.
A possible solution is to use the :join command with a range:
:?.?+1,/./-1join!
Explanation:
[range]join! will join together a [range] of lines. The ! means with out inserting any extra space.
The starting point is to search backwards to the first character then down 1 line, ?.?+1
As the 1 in +1 can be assumed this can be abbreviated ?.?+
The ending point is to search forwards to the next character then up 1 line, /./-1
Same as before the 1 can be assumed so, /./-
As we are using the same pattern only searching forward the pattern can be omitted. //-
The command :join can be shorted to just :j
Final shortened command:
:?.?+,//-j!
Here are some related commands that might be handy:
1) to delete all empty lines:
:g/^$/d
:v/./d
2) Squeeze all empty lines into just 1 empty line:
:v/./,//-j
For more help see:
:h :j
:h [range]
:h :g
:h :v
Short Answer: ()V)kc<esc>
In normal mode, if you type () your cursor will move to the first blank line. ( moves the cursor to the beginning of the previous block of non-blank lines, and ) moves the cursor to the end (specifically, to the first blank line after said block). Then a simple d) will delete all text until the beginning of the next non-blank line. So the complete sequence is ()d).
EDIT: You're right, that deletes the whitespace at the beginning of the next non-blank line. Instead of d) try V)kd. V puts you in visual line mode, ) jumps to the first non-blank line (skipping the whitespace at the beginning of the line), k moves the cursor up one line. At this point you've selected all the blank lines, so d deletes the selection.
Finally, type O (capital O) followed by escape to crate a new blank line to replace the ones you deleted. Alternatively, replacing dO<Escape> with c<Escape> does the same thing with one less keystroke, so the entire sequence would be ()V)kc<Esc>.
These answers are irrelevant after the updated question:
This may not be the answer you want to hear, but I would make use of ranges. Take a look at the line number for the first empty line (let's say 55 for example) and the second to last empty line (perhaps 67). Then just do :55,67d.
Or, perhaps you only want there to ever be one empty line in your whole file. In that case you can match any occurrence of one or more empty lines and replace them with one empty line.
:%s/\(^$\n\)\+/\r/
This answer works:
If you just want to use normal mode you could search for the last line with something on it. For instance,
/.<Enter>kkVNjd
I didn't test so much, but it should work for your examples. There maybe more elegant solutions.
function! DelWrapLines()
while match(getline('.'),'^\s*$')>=0
exe 'normal kJ'
endwhile
exe 'silent +|+,/./-1d|noh'
exe 'normal k'
endfunction
source it and try :call DelWrapLines()
I know this question has already been resolved, but I just found a great solution in "sed & awk, 2nd Ed." (O'Reilly) that I thought was worth sharing. It does not use vim at all, but instead uses sed. This script will replace all instances of one or more blank lines (assuming there is no whitespace in those lines) with a single blank line. On the command line:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile
Keep in mind that sed does not actually edit the file, but instead prints the edited lines to standard output. You can redirect this input to a file:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile > tempfile
Be careful though, if you try to write it directly to myfile, it will just delete the entire contents of the file, which is clearly not what you want! After you write the output to tempfile, you can just mv tempfile myfile and tada! All instances of multiple blank lines are replaced by a single blank line.
Even better:
cat -s myfile > temp
mv temp myfile
cat is awesome, yes?
Bestest:
If you want to do it inside vim, you can replace all instances of multiple blank lines with a single blank line by using vim's handy feature of executing shell commands on a range of lines within vim.
:%!cat -s
That's all it takes, and your entire file is reformatted all nice!

Yank first word from specific line

I figured out that :23y will yank the entire 23rd line.
But what I want to do is yank only the first word on line 23.
I tried :23yw, but that does not work. Is there an easy way to do this?
Can this be done without going to the line first and then yanking and then typing ` to go back to the line I was editing on?
23ggyw will do it. I don't think there's a quicker way.
Explanation: 23gg moves the cursor to line 23, yw yanks one word.
Note that this only works if you have the startofline option set (which is the default). Otherwise you need to explicitly move to to the first non-whitespace character: 23gg^yw.
The :y is an abbreviation of the :yank Ex command, that's why :yw does not work; it's a normal mode command. As the other answers have already shown, you can trigger those from the command line via :normal yw.
I'm afraid there's no way avoiding the jump in a practical way (but, as mentioned, <C-O> lets you jump back to the original position). You could use Vimscript:
:let #" = matchstr(getline(23), '^\w\+')
But that's hardly easier to type, and only suitable for a function.
I don't think there's a way to do that without moving the cursor.
Anyway, here is another way to do it:
:23norm! yw
Breakdown:
: because we are using an Ex command,
23 is the line on which we want to do something, it is a range of 1,
norm[al] executes a normal mode command on the given range,
yw yanks the first word.
Add <C-o> to go back to where you come from.
type 23Gyw in normal mode should do the job.
G Goto line [count], default last line, on the first
non-blank character |linewise|. If 'startofline' not
set, keep the same column.
G is a one of |jump-motions|.
Following would work without moving the cursor as requested but it's a hassle to type.
:23y|norm PJ0eld$
or you could try working out something with
:23t.|norm eld$
23jyw should be able to do it, it will take you to 23rd line and yank first word

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

Resources