Delete all but the first instance of a line - vim

How to delete all but the first instance of a line, which is known?
For instance, I have
LOADING CONDITION : LIGHTSHIP CONDITION
several of these spread out through the file's contents. I would like to keep only the first instance which is somewhere near the top.
Ideas anyone?

You can use the :global command combined with a range.
:0/LOADING CONDITION/+,$g//d
Explanation:
[range]g/{pat}/{cmd} run a command, {cmd}, on every line matching {pat} inside the giving line range, [range].
0/LOADING CONDITION/ starting with the first line find the pattern LOADING CONDITION
0/LOADING CONDITION/+1 start the range 1 line below the first instance
+1 can be shorted to just + because the 1 can be assumed.
,$ the end of the range will be the last line in the file which is refereed to as $
g// use the last search pattern. In this case the pattern from the range
:delete or :d for short is the ex command used to delete the lines
For more information see
:h :g
:h :d
:h range

gg (make cursor back to top)
/LOADING CONDITION : LIGHTSHIP CONDITION (enter)
n
:.,$g//d

My PatternsOnText plugin provides a command (and other related ones) that makes this very simple:
:DeleteDuplicateLinesOf /^LOADING CONDITION : LIGHTSHIP CONDITION$/

Related

What means is vim commnad ":v"or":g!" when it words on range

Vim manual says
:[range]v[global]/{pattern}/[cmd]
Execute the Ex command [cmd] (default ":p") on thelines within [range] where {pattern} does NOT match."
but when use command like ":v/{pattern1}/,/{pattern2}/[cmd]", the result is not what I want, and I don't understand why.
For exapler, there is a text file
1
2
3
4
5
When i execute the command ":g/2/,/4/d",then the line from "2" to "4" will be deleted.
It's fine.
But if i execute the command ":v/2/,/4/d", it's not work just like i know.
I thinkit should keep the three lines from "2" to "4", deleted others, but it not.
Or, I think the command ":v/2/,/4/d" may works like ":g/[^2]/, /[^4]/d", but it's not, too.
So, what's the exact mean about the command ":v/{pattern1}/,/{pattern2}/[cmd]"?
the command does what it should do. I think you didn't understand the command correctly. I try to explain it.
first, the :g one. You have: :g/p1/,/p2/d we should read the command as:
:g/p1/ "for each line match p1
,/p2/d "till line match p2, delete.
here you used a range, from line matching p1 till (, comma) line matching p2. with your 1-5 example, vim find the 1st matched line #2, then you have a 2,/4/d, so line 2-4 was removed. :g is not finished, it looks the rest line : the 5 line, it doesn't match /2/, next line, oops, hit EOF, so :g has done its job.
If you made an example from 1 to 20 , you would see some error msg Pattern not found, this happens because, the :g can find /2/, but the range ending /4/ could not be found any longer, it has been removed by last d command. Do a test by yourself, you will see what I meant.
If :g is clear, :v is easy to understand. :v/2/,/4/d
vim search first line doesn't match /2/, it would be the first line 1, then do 1,/4/d, that is, line 1-4 are deleted. :v command is not yet finished, it goes to the line with 5, which is line number 1 now, it doesn't match /2/ either, so vim take it, does a 1,/4/d, but there is no line matching /4/ in your buffer, so pattern not found error message will be displayed. And :v command finished its job.
You should keep in mind that, :g/{pattern}/cmd is not :g{range}cmd. pattern and range are different things. do a :h range to see detail
That's why you got the result. Hope it is clear.

Vim replacing in inverse direction

How can I replace in Vim from current line in inverse direction (upward) (for searching I'm using ?textToFind, for replacing from current position :,$s/a/b)?
You can use backwards range with :s command.
If you want to do replacement from line 1 to your current line, you can do :,1s/foo/bar/g vim will ask you if you are sure to apply command on a backwards range, press y
You can also do something like :,-3s/foo/bar/ to do replacement from current line (n) till line n-3
The range used for s// (and other Ex commands) can be made of:
line numbers, 1,23
relative lines, -5,+17
line shortcuts, .,$
marks, 'a,'g
searches, ?foo?,/bar/
or any combination of the above items, ?foo?,'g, 23,$, +5,/bar/, .,/baz/+6…
A range extending from the first instance of foo before the cursor to the last line could look like that:
?foo?,$
A range extending from the first instance of foo before the cursor to the current line could look like that:
?foo?,.
and even be shortened to:
?foo?,
There's no built-in way to visit the lines from the end to the beginning. Vim will issue a Backwards range given, OK to swap? query, and turn around the range if confirmed. The only way is by manually specifying the individual lines in reverse order:
:.s/a/b | .-1s/a/b | .-2s/a/b | ...
Of course, you can write a custom command for that.
If you also require reverse replacement inside a line (all of that only makes sense with the confirm flag, doesn't it?), you're out of luck with :substitute.

Concatenating numbers in vim

i have a series of hexadecimal numbers as shown below in colums.
cdef
89ab
4567
0123
I would want to arrange the numbers in one single row starting from the last row as follows.
i.e 0123456789abcdef. How can i get it done in vim without using macros?
The commands
Reverse the lines with
:g/./m 0
Join all the lines and the ! flag does not insert or remove white-space.
:%j!
The Explanation
The :global command takes the form: :g/{pat}/{cmd}. So run command {cmd} on ever line that matches pattern {pat}. In this case our pattern is . which matches a non empty line. Our command is :move. The :move {address} command will move a whole line to an address/line, {address}. In our case we are moving each line to the top of the file so we use 0.
All together the :g/./m0 will take every non empty line and move it to the top. Since the :global command runs from the top of the file to the bottom, the first line gets moved to the top first and the last line get moved to the top of the file last. Think of this kind of like a stack (LILO). Therefore the lines are reversed.
Now all that is left is the join all the lines together with out any extra white space. The :join command takes the form :{range}join[!]. We want to join from the first line, 1, to the last line, $, so our range would be 1,$. However this is very common so there is a shortcut for this range, %. The optional ! flag tells :join to not add or remove any white space. All together :%j! will concatenate the lines into one long line.
As a side note is probably more common to reverse the lines via :g/^/m0 as the ^ pattern matches every line not just non-empty lines.
For more help see:
:h :g
:h :m
:h :j
with Join plugin (https://github.com/sk1418/Join) you could just do:
:%J '' r
or
:J '' 4 r
r flag means join lines in reverse
to achieve the goal. It supports more features regarding line joining. check it out..
and.... that script was written by me. :P
Kent's plugin does the job and is recommended if you need to do this often; here's a (more long-winded) alternative with built-in commands:
First, use the :move command together with :global to reverse the lines.
:1,4g/^/m0
Then, join without any separator:
:1,4j!

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!

Specifying position for a command in Ex mode

I want to remove percentage marks from the following lines:
oh_test_() ->
[
%{"fold", ?_test(fold(ns()))},
%{"fold nested", ?_test(fold_nested(ns()))},
%{"push arg empty table", ?_test(push_arg_empty_table(ns()))},
%{"push arg table 1", ?_test(push_arg_table1(ns()))},
%{"push arg nested table", ?_test(push_arg_nested_table(ns()))},
%{"multicall 0", ?_test(multicall_0(ns()))},
%{"multicall 1", ?_test(multicall_1(ns()))},
%{"multicall 2", ?_test(multicall_2(ns()))}
].
Cursor is on the line with first %.
:,/%/s/%//
Or:
:,/%/normal ^x
Expected: all percent marks removed. Result: removes only first two percent marks.
Why?
How should I do it
Without using visual mode, and
Not counting line numbers?
Question 1:
Your range: ,/%/ is roughly translated starting from the current line. The end of the range will be the next line that matches /%/ after the current cursor line. This will yield the 2 lines. See :h :, for more information.
Question 2:
There are many ways to do accomplish this. You have already presented a normal and a s/// method. One way to fix your commands is to adjust the range. One of the following will work:
,/\]\./-1 match the ending ]. and then subtract a line
,/^\s*%\(.*\n\s*%\)\#!/ Use a negative look ahead to search for a line that does not start with a %.
All together you could use:
:,/\]\./-1s/%//
:,/^\s*%\(.*\n\s*%\)\#!/s/%//
:,/\]\./-1norm ^x
:,/^\s*%\(.*\n\s*%\)\#!/norm ^x
An alternative to using a macro. The nice thing about using macros in this case is that when an error occurs it stops. Basically you record a macro to search for the % and then delete it then move to the next line. Execute this macro a large number of times. When a % cannot be found the macro will stop.
qq0f%xjq999#q
The macro is my preferred method in this case as I do not need to do any crazy patterns or go looking for the end of the block.
If you allowed the use of visual mode I would suggest vi]k:norm ^x
For more information see:
:h range
:h :,
:h /\#!
:h q
:h #q
:h i]
You first example is basically saying... from my current position until the next % issues the command s/%//...
What you may want to do is something like
:,$ s/%//
which says, for each line from my current position till the end of the file ($), issue the command s/%//
If you didn't want to do it till the end of the file then you could
:set number
Which will show you line numbers, then do something like
:2,8 s/%//
which just issues the command for all lines from 2-8
You seem to be using :,/%/ in a way that should use :g/%/. You could use your command as such: :g/%/s/%// or :g/%/norm f%x
See :help :g for more information on the "global" command. This basically executes a command-mode command on lines matching the pattern. Your version, :,/%/ operates from the current line until the match of "%". This removes the first two because the current line has one and you're searching to the line that has one (the next line). You can read more about this in :help :range. A better way to use this option would be to use a search item that's only on the last line. For example, :,/]/s/%// or :,/]/norm f%x.

Resources