How to use the vi (vim) editor in linux

Donald Daniel, Feb 2008, revised Feb 2012

up one level

introduction

The vi editor is the most popular editor in linux. The current version is really "vim", but to invoke it simply type "vi".

Open a terminal window in your linux system and click on it to make sure it is active. You will do all your work in the terminal window. If you are not already familiar with how to execute programs, add missing commands and linux command line operations in a terminal window you should first read the article how to use linux .

To edit an existing file named "xyz", or create a new file named "xyz", type "vi xyz". When in vi, there are two major modes, the input mode and the edit mode. In the input mode, anything you type goes into the file. In the edit mode you can move around the file, copy, delete etc. Hit the "Esc" key to get from the input mode to the edit mode. When in the edit mode the mouse is not used to move the cursor, keyboard commands are used. The arrow keys move the cursor up, down, right, left. The spacebar moves the cursor to the right. "$" moves the cursor to the end of the present line. The "enter" key moves the cursor to the beginning of the next line. "237" then the "enter" key will move ahead 237 lines in the file, unless there are less than 237 lines in the file, in which case the cursor will not move. Type "1G" to jump to the top of the file, "G" to move to the end of the file.

When the cursor is moved to a particular letter in a word in the file, press "a" to enter the insert mode after that letter, press "i" to enter the insert mode just before that letter. Type what you want in the insert mode then hit the "Esc" key to leave the insert mode and get back to the edit mode.

To write the file to disk and exit vi, type ":wq". To exit without saving the changes you have made, type ":q!".

delete

In the edit mode "d" followed by the spacebar will delete one letter. "5 d" followed by the spacebar will delete 5 letters. "dw" will delete to the end of the word. "dd" will delete the line. "5 dd" will delete the next 5 lines. "u" will "undelete" the last deletion if you make a mistake. "d /xxx" will delete from the present position to where the letters "xxx" are found. Suppose you have a file with many lines that have leading spaces in front of each line, and you would like to delete these leading spaces. The command ":%s/^ //g" will do this, where the number of spaces after the "^" character is the number that you want to delete. The "^" character represents the beginning of a line. To delete all complete lines containing "xyz", use ":g/xyz/d". An example of how to delete carriage returns is given below in the section about line numbers.

moving text

Delete what you want to copy. If you want to leave it there before you move elsewhere to copy it, type "u". Then move where you want to copy it. "p" will write out the last deletion on the line below where ever you are when you type "p", as many times as you type "p". To join the line below to the end of the current line, type "J". Suppose you want to copy an external file named "file" to the line below where your cursor is. If the file is in the same directory, type ":r file". If I were in my directory "wkspc/scratch", and wanted to copy a file from my home directory, I would type ":r ~/file", but if I wanted a file from my directory "wkspc/back" I would type ":r ~/wkspc/back/file" or ":r ../back/file", either one would work. For explanation of this see how to use linux

find and replace

"/xxx" will find the next occurrence of "xxx". "n" will find the next one after that. "?xxx" will search backwards. The next example is "/\<cat\>". This will find "cat" without finding "catalog". This is very handy for finding variables in a program you are writing. ":121,135s/cat/dog/g" will replace all occurences of cat with dog from lines 121 to 135. ":%s/cat/dog/g will do the same for the whole file. But these will replace "catalog" with "dogalog". ":%s/cat//g" will delete all cat in the whole file. Another important example is ":%s/\<cat\>/dog/g". This will replace all occurrences of "cat" with "dog" without replacing "catalog" with "dogalog". This is very handy for changing variable names in a program. This command takes into account the conventions of computer programming. Thus "x:=cat+5;" would be changed, but "x:=cat2+5;" would not. Note that spaces are not necessary on either side of "cat". This is probably the most essential feature for any editor that is to be used for writing computer programs. If you are interested in learning to write programs click here. To search for the string "get / http" type "/get \/ http". This is because the the "\" means treat the next character as a character, not a command or a wildcard. If you have lots of different words that must be replaced by other words the "so" command can be used with a file listing the words and their replacements. An example is given in the "programming hints" article here.

commands that use line numbers

":nu" tells the number of the line that the cursor is on. ":13,26 w! temp" writes a copy of lines 13 to 26 to an external file named "temp" without deleting the lines. ":125,248 !fmt -uw 65" does word wrap to make all the lines from 125 to 248 equal to or less than 65 characters in length. To remove carriage returns such as windows files have from lines 22 to 44 type ":22,44 !tr -d '\r'". To make all letters from line 125 to line 248 lower case, use ":125,248 !tr A-Z a-z". Using the "!" symbol in this particular way when you are in the vi editor executes a program that is external to the vi editor. To see what else could be done with the "tr" program, when you are NOT in the vi editor enter "man tr".

There are many, many more commands in vi, but these are probably the most important.

spell checking

If you create the file xyz with the vi editor, you can correct the spelling errors with a program called "ispell". When you are not in vi, "ispell xyz" will find spelling errors, and ask you if you want to correct them. When finished, the file xyz will have errors corrected, and xyz.bak will be the original file with errors.

fancy text

The vi editor is adequate for writing computer programs and simple text applications. If you want to create a document that requires advanced features such as characters that are not on the keyboard like greek letters or math symbols, or embedded graphics in your document, there are two ways to do this.

The first way to create fancy text is in the form of a pdf document. The vi editor can be used to create the source file for a fancy word processing system on linux known as latex, which is found in the "texlive-latex-base" package that can be installed on linux. The instructions are in the "texlive-latex-base-doc" package. This technique is described here. An example of this at this website is given by the pdf document here.

The second way to create fancy text is as a png file to be imbedded in an html document. The vi editor can be used to write html web pages. This entire website was written with vi, and the html checked for errors with the linux program "tidy". You can see how to write html by saving this page to disk as an html file, and looking at the html file with the vi editor. A very simple example of fancy text is the following equation that includes a greek letter:

demo.png

It was produced with the vi editor writing a file of postscript code as follows:

%!PS
%%BoundingBox: 100 400 150 416
100 400 moveto
/Palatino-Roman 16 selectfont
(a = ) show
/Symbol 16 selectfont
(w) show
0 7 rmoveto
/Palatino-Roman 16 selectfont
save
0.8 0.8 scale
(2) show
restore
7 -7 rmoveto
(r) show
showpage
%%EOF

You may worry that you have not had a course in programming postscript. Do not worry, read this program and you will be able to see how it works. While we have the symbol font selected the letter "w" represents the greek letter omega.

The file was created without the two lines containing "%%". The "gv" program was used to see our file as we created it and debugged it. As first created the postscript file represents a whole page with the text on it. We do not want a whole page, we just want a small snippet of text. So we must convert it from a plain postscript file to an encapsulated postscript file. We use the program "ps2epsi" to produce an epsi file line 8 of which was the first line containing "%%" that we added to our file, then we added the second line containing "%%". We then discarded the epsi file. Now our file is encapsulated postscript. Then start the "gimp" program, "file" "open" "import" the eps file, then "file" "save as" change the suffix from eps to png, then "save". Now we have a good png version.

Some symbols in the symbol font are beyond the range of the keyboard, and must be specified by an octal number. For example, if you search the web for the symbol font, you can find that the decimal number for the first character of the square root sign is 214. If you install the wcalc command, "wcalc -o 214" gives the octal equivalent 0326. In your postscript file "(\326) show" will produce the character.

printing

After you have finished file "xyz" and got out of the vi editor, you could print it with "lpr xyz". However, this might not be very satisfactory, because the printout would have no left margin, no top and bottom margins, and no page numbers. A slightly better result is obtained with the command "pr -fl 56 -o 8 xyz > temp". Here 56 is the number of lines arbitrarily chosen to be printed on each page. You can use the vi editor to see if the file "temp" is properly formatted, then "lpr temp" to print it.

The above technique for printing uses built-in software but is a bit crude, so I prefer a different technique. To print out programs with margins and page numbers, I use my print program, given here as Oberon-2 source code. The program does not discard the end of lines that exceed the right margin, it truncates them and wraps the extra part of the line to a new line. The combination of the vi editor and the print program makes a simple text only wordprocessor. This print program does not send the text directly to the printer. It creates a new formatted file called "pfile", which you must then send to the printer with a command such as "lpr pfile". The program can combine multiple files into one pfile with continuous page numbering. It can force a new page at the start of a new file if you wish. From beginning to end, the commands to use this program to print file xyz are: prt, fn xyz, go, q, lpr pfile.

MODULE prt;
(*written 1980 donald daniel. Originally in pascal,
converted to Oberon in 2008.  This program is free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software
Foundation, either version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General
Public License along with this program.  If not, see
http://www.gnu.org/licenses/.  *)
IMPORT In,Out,Msg,Files,TextRider;
CONST strlngth=10;strlngth2=50;bell=7;formfeed=12;lf=10;cr=13;
fn=1;tm=2;lm=3;sp=4;sl=5;pl=6;ll=7;go=8;ff=9;pn=10;
npn=11;menu=12;init=13;q=15;er=20;
TYPE 
str=ARRAY strlngth OF CHAR; str2=ARRAY strlngth2 OF CHAR;
cma=ARRAY 22 OF str;
VAR resv:Msg.Msg;invar,outvar:Files.File;
infile:TextRider.Reader; outfile:TextRider.Writer;
cmdara:cma; command:LONGINT; filename: str2;
strng:str; pagenum,line,pagelength,linelength,
topmarg,leftmarg,centerpage,chnum:LONGINT;
ch:CHAR; numpg,topofpage,leftofpage:BOOLEAN;

PROCEDURE readword(VAR s:str);
VAR ch:CHAR; i:LONGINT;
BEGIN
In.Char(ch);WHILE ch=' ' DO In.Char(ch);END;
i:=0;REPEAT s[i]:=ch;INC(i);In.Char(ch);
UNTIL ((ch=' ')OR(ch=0AX)); s[i]:=00X;
END readword;

PROCEDURE readln(VAR s:str2);
VAR ch:CHAR; i:LONGINT;
BEGIN
In.Char(ch);WHILE ch=' ' DO In.Char(ch);END;
i:=0;REPEAT s[i]:=ch;INC(i);In.Char(ch);
UNTIL ch=0AX; END readln;

PROCEDURE leftmg;
VAR i:LONGINT;
BEGIN FOR i:=1 TO leftmarg DO outfile.WriteChar(" ");END;
leftofpage:=FALSE END leftmg;

PROCEDURE topmg;
VAR i:LONGINT;
BEGIN
FOR i:=1 TO topmarg DO outfile.WriteLn;END;
chnum:=1;topofpage:=FALSE;leftofpage:=TRUE END topmg;

PROCEDURE newpage;
BEGIN
centerpage:=linelength DIV 2 + leftmarg;
IF topofpage THEN topmg;END;
WHILE line<=(pagelength-2)DO
outfile.WriteLn;line:=line+1 END; outfile.WriteLn;
IF numpg=TRUE THEN outfile.WriteLInt(pagenum,centerpage);END;
outfile.WriteLn; outfile.WriteChar(CHR(formfeed));
line:=1;chnum:=1;pagenum:=pagenum+1;topofpage:=TRUE;
leftofpage:=TRUE; END newpage;

PROCEDURE outchar;
BEGIN 
IF ORD(ch)=lf THEN outfile.WriteChar(CHR(cr));END;
outfile.WriteChar(ch);chnum:=chnum+1;END outchar;

PROCEDURE inputline;
BEGIN
WHILE (~ infile.Eol()) DO 
IF topofpage THEN topmg;END;
IF leftofpage THEN leftmg;END;
infile.ReadChar(ch);
IF ORD(ch)=formfeed THEN newpage ELSE outchar; END;
IF ((chnum>linelength)&(~ infile.Eol())) THEN line:=line+1;
IF(line>(pagelength-2))THEN newpage ELSE outfile.WriteLn;
leftofpage:=TRUE;chnum:=1;END;END; END(*while*);
infile.ReadLn;outfile.WriteLn; 
leftofpage:=TRUE;line:=line+1;chnum:=1;
END inputline;

PROCEDURE printfile;
BEGIN
chnum:=1;
invar:=Files.Old(filename,{Files.read},resv);
infile:=TextRider.ConnectReader(invar);
WHILE ~(infile.res#Files.done) DO inputline;
IF line>(pagelength-2)THEN newpage;END; END(*while*);
invar.Close; Out.String('end of file');Out.Ln;
END printfile;

PROCEDURE determine(VAR cmdvar:LONGINT);
VAR cmi:LONGINT;strvar:str;
BEGIN
cmdvar:=er; readword(strvar);
FOR cmi:=fn TO q DO 
IF (cmdara[cmi]=strvar)THEN cmdvar:=cmi;END;END;
IF cmdvar=er THEN Out.Char(CHR(bell));
Out.String('<---<< error');Out.Ln;END;END determine;

PROCEDURE initvar;
BEGIN
cmdara[fn]:='fn'; cmdara[tm]:='tm'; cmdara[lm]:='lm';
cmdara[sp]:='sp'; cmdara[sl]:='sl'; cmdara[pl]:='pl';
cmdara[ll]:='ll'; cmdara[go]:='go'; cmdara[q]:='q';
cmdara[ff]:='ff'; cmdara[pn]:='pn'; cmdara[npn]:='npn';
cmdara[menu]:='menu'; cmdara[init]:='init'; filename:='';
leftmarg:=10;topmarg:=6; pagenum:=1;line:=1;
linelength:=65;pagelength:=53; topofpage:=TRUE;numpg:=TRUE;
END initvar;

PROCEDURE menuproc;
BEGIN
Out.String('MENU');Out.Ln;
Out.String('fn filename [enter]'); Out.Ln;
Out.String('pn for page numbering or npn for no numbering=');
IF numpg THEN Out.String('pn') 
ELSE Out.String('npn');END; Out.Ln;
Out.String('the next few items may be entered'); Out.Ln;
Out.String('as item [space] value [enter]'); Out.Ln;
Out.String('sp startpage='); Out.LongInt(pagenum,2);Out.Ln;
Out.String('ll linelength='); Out.LongInt(linelength,2);
Out.String('  pl pagelength=');Out.LongInt(pagelength,2);Out.Ln;
Out.String('tm top margin=');Out.LongInt(topmarg,2);
Out.String('  lm left margin=');Out.LongInt(leftmarg,2); Out.Ln;
Out.String('the remaining items have no value');Out.Ln;
Out.String('go to process file. You can process as many');
Out.Ln;Out.String('files as you want before you type q.');
Out.Ln;
Out.String('ff to formfeed before starting next file');
Out.Ln;
Out.String('menu to see this menu');Out.Ln;
Out.String('init to reinitialize to default values'); Out.Ln;
Out.String('q to quit processing files and write result to pfile'); 
Out.Ln;
Out.String('lpr pfile to print when finished');Out.Ln;
END menuproc;

BEGIN
initvar;
outvar:=Files.New('pfile',{Files.write},resv);
outfile:=TextRider.ConnectWriter(outvar);
menuproc; determine(command);
WHILE command # q DO
CASE command OF 
fn: readln(filename);| 
sp: In.LongInt(pagenum);In.Line(strng);|
pl: In.LongInt(pagelength);In.Line(strng);|
ll: In.LongInt(linelength);In.Line(strng);| 
pn: numpg:=TRUE| npn: numpg:=FALSE| 
tm: In.LongInt(topmarg);In.Line(strng);|
lm: In.LongInt(leftmarg);In.Line(strng);| 
go: printfile| ff: newpage| menu: menuproc| 
init: initvar|
er:|END(*CASE*);
determine(command) END(*WHILE*); END prt.

up one level