Regarding the vi versus emacs debate, I like this letter by Tim O'Reilly:
http://www.oreilly.com/pub/a/oreilly/ask_tim/1999/unix_editor.html.
So, what will you get from reading this Web page? Well, besides the usual
pointers to things written by other poeple, I have some interesting tips
and tricks, and one really "geek chic" macro to share.
vi Books
A Guide to vi is the book I used to learn vi, but is unfortunately
out of print. Perhaps you can find it in a used bookstore somewhere:
Editions 1 through 5 were authored by Linda Lamb, and these are now out of print. Arnold Robbins name appears with Linda Lamb on the 6th edition's list of authors, but Tim O'Reilly helped too: "While I don't have an author credit on the cover, I actually wrote a large part of the fourth and fifth editions, including the opening paean to the virtues of vi." -- Tim O'Reilly, Jun 21 1999, in response to an "ask Tim" email.
vi Help
vi
(pronounced vee-eye, as most people think of it), is a unix-based program
which has a certain guaranteed subset of functionality. What this means is that
help for vi should be useful as help for other programs--such as Elvis and Vim--which
are based upon vi. Here are some Web-based helpers to using it:
Using vi with other Tools
To use vi as your command line history editor at the Unix shell, you need to
tell the shell that this is your preference.
At the bash Shell prompt: set -o vi
At the Korn Shell prompt: set -o vi
At the tcsh Shell prompt: bindkey -v
To get into the vi editor to look at or modify your history, simply type <ESC>k and then you move up or down in the stack with j and k. Note that searches should be done with ? when you first go in, because you are at the bottom, not the top.
To use vi as your editor for the Pine email tool, simply go to the
configuration setup (M for main menu, S for setup, C for configuration, then
use w to search for 'editor') and set the editor to vi. In the .pinerc file
it will appear thus:
# Specifies the program invoked by ^_ in the Composer,
# or the "enable-alternate-editor-implicitly" feature.
editor=vi
Also, the following features should be enabled, or added to the .pinerc
features list (e.g., features=)
vi Tricks
There are a few things which are not obvious from reading the man pages,
FAQs, quick-reference guides, and so-on, which are too cute not to mention as
anything else but a trick. In here I've listed some I've come to like.
But first I'll mention a few things. :g/search-string/ can be placed in front of several ex commands, and ex underlies vi. The two most common commands I'd use here would be m and d, with 'd' to delete lines which are matched up, and 'm' used to move the matched lines to somewhere. As an example of the latter, :g/X-/m0 will move all lines which contain 'X-' to the top of the file. But the trick here is that the 'g' may be replaced with a 'v' to cause an inverse effect. In other words, all lines which do not match are affected by the command.
Also, many people use the 'u' command to undo. But few use the 'U' command. The difference is that 'u' undoes the last change, but 'U' undoes all changes made to the current line (so long as you are still on it).
a/&_<esc>"add:@a
Before you start using the .exrc file, check out this
advice from Sandy Herring
<sandy@herring.org>:
Please note, these lines should *not* be cut-and-pasted into your
.exrc file. Each should be entered with vi, so that the two-
character sequences '^M' and '^[' which appear here are correctly
created as the single control characters they really are. They
are created as follows: each '^[' should be entered as control-v
followed by the escape-key, and each '^M' should be entered as
control-v followed by control-m (vi allows actual control
characters to be entered in command lines as long as they are
preceeded by control-v).
If you would like to save yourself all of the trouble of typing
this in, you can simply bag my .exrc file
in uuencoded format.
Command String
Description of Action
:v/./d or :g/^$/d
Delete all empty lines. Add match for blanks if required.
:%s/^$/^M
Add a new blank line to all existing blank lines. ^M is control-v, control-m
:v/^flort/s/^t&/
Insert a tab before any line that does _not_ with 'flort'. ^t is actually a tab character
:/flort/,/flortx/d
Delete from the first occurance of 'flort' to the first occurance of 'flortx'.
:/flort/:r filex
Find the first occurance of 'flort' and insert under that line the contents of 'filex'
:/flort/:r !date
Find the first occurance of 'flort' and insert under that line the output of the shell command 'date' (or any other shell command)
:g/./join
One of my favorite discoveries. Joins each pair of lines.
:v/^str/-1join
Created from a question by John Lenning
:v/^...........$/d
Created by John Lenning while working on Puzzle #35 ("They've got it all") of Matt Gaffney's Weekly Crossword Contest. Answers in the grid provided clues to the first and last names of 3 people who's collective names were pangrammatic. One was an ex-NHL allstar for 11 letters. John copied a list of players from a web page and pasted them into a vi session, then after removing white space used this command to delete all lines that did not have eleven letters. My version of that was to take the cleaned-up file and use "awk 'BEGIN { FS=RS } {print length, $0}' scratchfile | sort -n" (awk puts length numbers in front of each line) but I think John's is much more elegant.
:v/./.,/./-1join
Replace two or more blank lines with one blank line. :v is like :g except that the regex (which is /./ in this case, or match a character) is inverted for the global scope. Blank lines do not match.
:.,/^$/g/./.,/^$/j
This has the same scope as !} (e.g., a paragraph) but it allows the joining of all lines in it. 'j' is the same as 'join' and is an ex command. Since !} takes you out to the shell, we do this odder format to get to the ex command underlying vi.
:.,/^$/-1j
If you liked the previous :.,/^$/g/./.,/^$/j,
you'll really like this shorter, alternative form, which
essentially does the same thing. Adding the -1 fixes a
problem the new form introduces, which is to strip out the
white space as well. The minus one leaves it there. I
store it in my .exrc file as map =j :.,/^$/-1j^M using
control-v control-m to get the ^M.
:.,$g/./.,/^$/-1j
Here we have taken the "join paragraph lines" formula in
its longer form of :.,/^$/g/./.,/^$/j and modified the
file-scope from paragraph to current position to
end-of-file (assuming there is a blank line at the end of
the last paragraph). All paragraphs delimited by
white-space lines will become one-line paragraphs
separated by the white space.
:.,+2j
Join the current line with the next 2 (3 lines joined).
:g/.*/.,+2j
Like the previous join but global. The /.*/ matches all lines, and every 3 lines are joined. If you have no blank lines, you can simply use /./ instead. Inspired by a question from JJ at the University of Alcala, Spain, in August of 2006.
o%s/.<esc>x9p
JJ again. He wanted to be able to insert a character after a particular column (which is KNOWN to exist). What this does is build a statement like '%s/........./&_' on a new open line, which is then deleted into buffer 'a' and then executed via ex as '@a'. The number 9 is arbitrary (could be any column), and the underscore could be any character or string. But the columns (its global) must exist or you get a pattern matching error. This beats using a mouse, IMHO :-). the '.<esc>x9p' part puts '.' in the buffer and then '9p' pastes it on the line 9 times (you could just type them all in).
:g/rex/s/$/str/
(where 'rex' is short for 'regex'). On any line in the file which contains the regular expression specified then append to those lines only the string 'str'. Many variations on this formula can exist and be interesting.
:.!fmt
Format the current line only (Unix)
!}fmt
Format the current paragraph, (e.g., to next whitepspace; UNIX)
!3}fmt
Format the current and next two paragraphs (or next n-1; UNIX)
:.,/regex/!fmt
Format from the current line to matching regex, say, a word (UNIX)
!Gfmt
Format everything from the current line to the end of the file (UNIX)
:%s g
Do a previous substitution on a global basis. Great for when you forget the % (which is really 1$) the first time. Doh.
:.,$s g
Do a previous substitution from the current line to the end of the file.
:s g
Do a previous substitution globally on the current line only.
:%s
Do a previous substitution on the first matching occurance of all lines, but not globally on the line.
:g/flort/s/foo/bar/g
Replace all occurances of 'foo' with 'bar' BUT ONLY on lines which contain the string 'flort'.
xp
Swap the location the character at the cursor and the one following.
ddp
Swap the order of two lines.
uu
Go to the location of the last change made. It is actually simply two
undo commands, but it has that effect if you are elsewhere in the file than
the last change made. This tip is from Victor Langeveld, who cautions, "'uu'
doesn't work with vim: in vim you'd use 'u^R' with the default settings."
:g/.*/m0
Reverses the order of the lines in a file.
ma
Mark a location you want to come back to with marker a (or b,
or c, or d...). Use in conjunction with 'a (or 'b, 'c, 'd, etc...).
'a
After moving to another part of the file, you type single-quote followed
by the marker letter (a, b, c, d, etc.). to go back.
''
Typing in two single-quotes after a search or nG (where n is {1,2,3...n})
will return you to where you were before the search. Very handy for
going off and double-checking something else in the file, then returning
to where you are currently editing. Also, doing '''' (four single-quottes)
as '' <pause> '' will allow you to toggle back-and-forth between
the two locations, visually comparing. Doing an update of some kind at the
searched-to location does not seem to affect this operation.
s/./\u&/g
This makes all the characters on the current line upper-case.
Replacing the '\u' with \l will do the reverse. I found this
on Nick Gammon's vi page, but he uses s/\<./\u&/g. I found
that dropping the '\<' seemed to have no effect in vi on SunOS.
:.! tr '[a-z]' '[A-Z]'
Another way to make everything on the current line upper-case. Note, however, that this can be of the form !) or !} (i.e., !}tr '[a-z]' '[A-Z]') to change either the current sentence or the current paragraph.
. and &
Simply typing in a period '.' after doing some sort of simple change
such as r or x will cause the last change to be repeated.
This is very useful for changing a numeral or letter in a column of text,
by doing j. over and over again (go down one line in the same column
and do the last one-character change again). The '&' repeats the last substitute using s/regex/replacement/ but remember :%s g to do it globally.
d0
This is not really a trick, yet it is amazing how many people are ignorant
of it. It is more of a practice. I use d0 all the time to delete
text prior to my cursor position and up to the start of the line. The analog
is d$, or delete until end-of-line.
dG (and dnG)
This also is not a trick, and like d0 it is amazing how many people are ignorant
of it as novices. And in fact, I looked at three fairly good vi "cheat sheets" that
came off of some pretty thorough vi web sites that did not mention it at all. But
anyway, this will delete from the currrent line to the end of the file. Bam! Just
like Emmeril adding spices to a dish. dnG deletes from the current cursor position
to line 'n'. So d1G will delete to the top of the file.
ctd
... where 'd' is any delimiter. Use this for changing text between two
quotes, for example. Place your cursor on the 1st character after the open
quote, do ct", then type in the new text (such as a URL). Then hit
<ESC> to pop out of change mode and back into edit mode. The trailing
" will be preserved.
H:.,+22list
If you execute this you will first move to the top line on the current
screen, then all lines on the screen will have a dollar-sign '$' placed
at the end, and you will be prompted by the message, [Hit return to continue].
When you hit return, (enter key), you will be back in normal mode again.
Useful for seeing if there are trailing blanks at the end of the lines.
To check just the line you are on, you can do :.list or simply go
to the end of the line with $.
:.= and := :.= gives the current
line number, := shows total lines in file. So does
<ctrl>G.
vi macros and the .exrc file
Let's begin with my .exrc file, which gives me some interesting leverage, because it contains macros I use:
set showmode
set ignorecase
" set directory=/var/tmp
"
map , :n^M map =i :r $HOME/.include.headers^M
map =dd :!rm %^M^M:n^M
map K $a ^[Bi^M^[y$$pBhxi^M^[$ma^!`awc -m^Md0i(^[$a)^[kJkJ$
map =f !}fmt^M map =s :r ~/.signature^M
map =w :w^M:n^M
map =c >>d0:co.^Mk:s/./x/g^M40A x^[079lD:s/x//g^MJ
map V :w^M:!ispell -x %^M:e!^M^M
An explantion for each line of the sample .exrc:
--------------------------- Begin Include ------------------------------
--------------------------- E N D Include ------------------------------
to move to the next one. The '^M' is created by entering in
<ctrl>v followed by <ctrl>m.I recently found a way to put the cursor back at the end of the word you were on when you started the fmt, after being asked about that by Ola Rauer <Ola.Rauer@med.lu.se> This is it:
map F BEa /flort+x+y+z^[B"mdfzhpBx{!}fmt -68^M@m^MdfzxBE
Basically, it adds a tag (which we hope is unique: 'flortx+y+z') and then stores it in buffer m as /flortx+y+z which it can later use to find that spot again, deleting the added tag. I think I'd change it so it does not go to the beginning of the paragraph but rather begins the fmt from where you are, but that is not what I was being asked about.
Why have something like this? Well, let's just
say that it has something to do with a prediliction for
crossword puzzles and entering-in memorable words in a clues
file. :-)
Before I explain how it works, let me say that 'ddPp' to duplicate a line (delete, paste upwards, paste downwards) nor 'Yp' will work in a global macro in the standard vi, so if you are wondering about the rather odd sequence of y$$a ^[pBhxi^M^[ you now have your answer, and if you don't get that, it is explained in two steps, below.
So, let's say I start with this line:
Execrable: VeryBad (7); Wretched (8)
and I want to add the word 'Detestable', so I have $a and I have
Execrable: VeryBad (7); Wretched (8); Detestable
so now I do the macro K and I wind-up with this:
Execrable: VeryBad (7); Wretched (8); Detestable (10)
Pretty neat, eh?! It is pure geek chic!
map =c 080i ^[$78hd0:s/ / /g^M
The =c center macro functions as follows:
Pretty simple, huh? Remember, this will replace the contents of the line it
was executed on, so use it on a blank line if you don't want to replace something.
:r !perl -e 'for ($i=1;$i<8;$i+=1) {print ".........$i";} print ".........\n";'
you get this ruler instead!
David Harnick-Shapiro of the University of California, Irvine ICS department
<david@ics.uci.edu> makes
the following suggestion:
Victor Langeveld <ltvic@mbfys.kun.nl>
also wrote me about creating an abbreviation which maps onto a command and in
effect acts like a macro, but in input mode. The effect is that as you are typing
text in the input mode, whenever you type DATE the word is replaced with today's
date, and you stay in input mode! Here it is:
:map! ^P ^[a. ^[hbmmi?\<^[2h"zdt.@z^Mywmx`mP xi
This is a clever little devil. What is happening here is that a command
is being built in a buffer from text in the macro and text in the file,
then is executed. Nowhere have I found a good explanation of this, so
I decided to document it myself (I did not invent this mapping).
The bang sign after the map tells vi to only pay attention to control-P
or control-N when you are in input mode, so for these to work you have
to be typing in a word.
The escape invoked takes it out of input mode, puts it into append mode
and adds a '.' after the word you were typing, then escapes out of that.
h seems not required, but is intended to make sure you are on the word.
The b moves the cursor to the start of the word and marks the spot using
mark 'm'. Back into input mode at the start of the word, we add in the
string '?\<' and escape-out. "zdt. deletes what we just typed in, through
the end of the word, to the period '.' and stores that result in buffer z.
@z
Here is another version that is supposed to do the same sort of thing. In
this case the author mapped it to control-Q(Ola Rauer
<Ola.Rauer@med.lu.se>
sent this to me, having found it at http://georg.f-451.net/elvis/#word_completion).
This document may be found at:
Other Tricks
Recovering Deleted Text
Ever make a change, then another, and regret the first one? If they are on the
same line, a U will take care of that. But if you have deleted two lines,
then discovered that you did not want to delete the first one, it is still available!
There is a stack of buffers which accept deleted text, numbered 1-9. The first
delete loads 1; the second delete moves 1 to 2 and loads 1; and the third delete
moves 2 to 3, 1 to 2, then loads 1... and so-on. You can paste these lines back
in by doing "np (that is, double-quote, number 1-9, letter 'p'), where 'n' is the number of the buffer you want (e.g., n deletes ago...), up to 9. Just doing, say, '2p' without the double-quote will paste the default buffer's contents twice, which is not going to help.
Recovering a File
If you execute :preserve a copy of your file--as it was at the last
write--will be stored in a special location (/var/preserve/user-id on Sun
Solaris, for example). You will also get an email about it, telling you it can
be recovered from the command line. But you can also recover it in vi at any time
by typing in :recover. So, let's say you do the preserve, then make a bunch
of changes, then want to see what the original file looked like. Do a :w
to write-out the current file, then a :recover to see the previous version,
then a :e! % to revert to the changed copy you wrote out. Warning:
once you do the recover, the preserved copy is removed from the system. But an
obvious use of recover would be to allow you to revert without having to run a
copy at the Unix shell prompt before you begin your edit session.
Reading in from the shell
I have another command I use quite frequently with vi, which is to read in something
from the shell. While this is quite an obvious trick when one reads the command
reference, I find that many folks miss this one. Use this whenever you want to
include text which is the output of a command. Let's use, as an example, the case
where one wants a copy of the current months calendar:
:r !cal
or
!!cal
Appending the current line elsewhere
Here is one more I often use; the case is when I am editing a file and want one
line to go on to the end of another file. First move to the line in question,
then do the following:
:.w >> filename
Writing out lines between marks
If you want to write out a file using the lines from one mark to another, there
is an easy way suggested by John Burns (john.h.burns@boeing.invalid_address_for_spammers_remove_to_reply.com).
Using the m mark command, and a pair of the twenty-six available markers
(a to z), you can mark an area of the file you are in, say ma and mb,
then write that section out:
:'a,'bw filename
or
:'a,'bw >> filename
to append.
Getting a chunk from one file to another
Often-times, one finds that one wants to take something from one file and put
it into another. Let's say that one wants a paragraph from file foo to be put
into file bar. Here are the steps:
Context jumping
Ever went someplace in your file, but wanted then to go back to where you where?
This is easy; just type two back-quotes, as in ``, and you will jump back to the
previous context. Within the context of one window, H takes you to the home-line,
L takes you to the last, and M to the middle of the window.
Creating a horizontal ruler
Ever want to know what character space you are on a line? What I do is to create
a horizontal ruler, then store it in one of my non-volitile paste buffers for
later use in the session.
If you have perl on your system, replace step one (7i.........!) with the following:
Shell commands to operate on a line, range or paragraph
I mentioned use of the !}fmt command during the discussion of the .exrc
file. Using fmt as an example again, here are the three common ways to
use it:
Of course, any reasonable shell formatting command may be used in this way, and
fmt itself may be used with options, such as fmt -60 to change
from the default line length to sixty.
Getting the tcsh ^T command to work in vi
Some tcsh users have gotten to like the ^T command which swaps (transposes) the
previous two characters. In vi, a single-character delete followed by a paste
will transpose, but here we assume one is doing input and types two characters
in backwards.
which simply drops out of input mode, swaps the chars, and throws you back into
input mode. Note that ^T is already used in vi, for tags, so you programmers might
want to take up my convention of =t for this one.
Using abbr to convert DATE to the date in vi
Remember to preceed the three control characters (i.e., ^M, ^[ and ^M) with a
ctrl-v. Victor has tested this only on FreeBSD. I have verified that it functions
as expected on SunOS 5.7 and Solaris 2.5.0.
Creating on-the-fly commands based on file text
Something I've run into on the 'Net and which after a search I find
exists in a number of places is this mapping:
In summary,
:map! ^P ^[a. ^[hbmmi?\<^[2h"zdt.@z^Mywmx`mP xi
reads to me like:
:map! CONTROL-P ESCAPE hbmmi?\< ESCAPE 2h"zdt.@z^ CONTROL-M ywmx`mP xi
With the spaces before and after ESCAPE and CONTROL-M (return) added for clarity of reading.
:map input ^Q ^[bmz?\<\@^Mnye`zPldea.^[bis^["zdt.@z
Scripts and vi
As has been seen with the format command, it is easy to pass
out a line, a paragraph, a range of lines--or the whole document, to an external
command for processing. The output of the command replaces the scope of the command.
It is thus possible to create custom shell scripts for specific purposes.
The vi Search File Script
John Burns <john.h.burns@boeing.invalid_address_for_spammers_remove_to_reply.com>
contributes this one. The .exrc file is loaded with a macro which, in
John's .exrc file, is a control-A. This command causes the first three
non-blank characters on the current line to be used as a regular expression for
searching a template file. A match on a template for a function call, for example,
could be used to replace the current line when programming.
map ^A !! $HOME/.vi.script ^M
sed -e 's/^ *\(...\).*/\1/' >/tmp/b
grep -i ^`cat /tmp/b` $HOME/.vi.search.file | fmt -72
Special Thanks
Special thanks to Sandy
Herring, for proof reading, and testing; Pete
Mastren, for his spiffy centering macro; Joshua Wright <jwright@jwu.edu>
for the original centering macro; Victor Langeveld <ltvic@mbfys.kun.nl>
for the tip about using uu to find the location of your last change, and for the
DATE abbreviation idea; and John Burns (john.h.burns@boeing.invalid_address_for_spammers_remove_to_reply.com)
for interesting tips, and suggesting that the .exrc file be available uuencoded.
Thanks also to all the folks who have shared vi tips with me over the years.
http://www.nacs.uci.edu/indiv/gdh/vi
Comments and suggestions welcome.
Last revised
Wednesday, 16-Sep-2009 16:36:12 PDT
.