How to express the line before the last in vim? - vim

How to express the line before the last in vim?
It is clear that $ is the last line.If there is a article which contain 20 llines,$ is equal 20 here, how about the line before the last,it is 19 ,how to write it such as $-1 ,can we create a expression 19=$-1??

If you want to reference the last minus n line in command line you can use $-n.
For instance, to go to the second to last line:
:$- # same as :$-1
You can use it to define a range:
:,$-10 # from the current line to 10 lines before the last one

It is a bit unclear what you are asking.
A solution to reach the penultimate line is using something like :
:normal Gk
that you can map in a user defined command.

Related

How to replace current string line with another line in 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

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!

Search and replace in vim in specific lines

I can use
:5,12s/foo/bar/g
to search for foo and replace it by bar between lines 5 and 12. How can I do that only in line 5 and 12 (and not in the lines in between)?
Vim has special regular expression atoms that match in certain lines, columns, etc.; you can use them (possibly in addition to the range) to limit the matches:
:5,12s/\(\%5l\|\%12l\)foo/bar/g
See :help /\%l
You can do the substitution on line 5 and repeat it with minimal effort on line 12:
:5s/foo/bar
:12&
As pointed out by Ingo, :& forgets your flags. Since you are using /g, the correct command would be :&&:
:5s/foo/bar/g
:12&&
See :help :& and friends.
You could always add a c to the end. This will ask for confirmation for each and every match.
:5,12s/foo/bar/gc
Interesting question. Seems like there's only range selection and no multiple line selection:
http://vim.wikia.com/wiki/Ranges
However, if you have something special on line 5 and 12, you could use the :g operator. If your file looks like this (numbers only for reference):
1 line one
2 line one
3 line one
4 line one
5 enil one
6 line one
7 line one
8 line one
9 line one
10 line one
11 line one
12 enil one
And you want to replace one by eno on the lines where there's enil instead of line:
:g/enil/s/one/eno/
You could use ed - a line oriented text editor with similar commands to vi and vim. It probably predates vi and vim.
In a script (using a here document which processes input till the EndCommand marker) it would look like:
ed file <<EndCommands
5
s/foo/bar/g
7
s/foo/bar/g
wq
EndCommands
Obviously, the ed commands can be used on the command line also.

Delete all but the first instance of a line

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$/

How to apply a regex to multiple lines?

let's say I want to apply the following to lines 60 through 80 in a file that I am editing in vim.
:s/{/ {/
Is there a way to do this?
Thanks!
You can prefix your search command with a line range, e.g.
:60,80s/{/ {/
If you want the change to apply from line 60 and forward use :60,s. If you want to change the lines from current to 80 do: :,80s. You can even do :.+3,80s to apply the search between current line + 3 and line 80.
You can use several type of ranges as described here. And here are the official docs.
You can also mark the first line of the section that you want to apply the regex to with the m (mark) command. Then move the cursor to the last line of the section. Then the regex command becomes:
:'a,. s:/foo/bar/
which says, from the line marked 'a' to the current line, substitute foo with bar.

Resources