Introduction
Vim is my editor of choice. I love it, I use it everywhere I can.
Problem is, It’s quite heavy and I spend quite a lot of time working on
embedded devices where it’s not available. Fortunately, I have busybox
on all of the devices and this means I have access to busybox vi
command.
Busybox vi
is very simple implementation on vi
editor which Vim
is
based on. So it uses the same editing philosophy as Vim
but it just
have much less features. Still, I like to use it to the fullest.
There is no documentation about busybox vi
that I’m aware of but since
its code is short, I could easily find out what is available. I will
start from the basics so that you should be able to use the editor even
if you never used vi
before. I will also provide some tips on how to
use it more effectively.
Since busybox vi
is so simple, this tutorial is aiming to document all
of its possible features (at least as of this writing). If some feature
is not documented, please report a bug. All of the things documented
here should also work in Vim
(I will try to add notes if there are
some noticeable differences between the two) so I believe this could be
used as a quite good intro to using Vim
too.
Notes
This tutorial assumes that you have busybox vi
with all additional
features enabled. Note however, that VI_REGEX_SEARCH is disabled in many
installations which disables using regular expressions when searching.
Every time I use vi
name, I’m referring to busybox vi
, not the
original vi
.
Some basics
Modes of operation
It’s important to understand that vi
is modal editor. This means that
your keystrokes will be interpreted differently depending on which mode you
are in right now. This is very powerful feature but it is also a
headache for those who never used modal editor before.
Busybox vi has only three modes of operation:
- command mode (with a colon command submode)
- insert mode
- replace mode
command mode
is the default (that’s why in it’s called normal mode
in Vim
). You can always return to this mode using ESC
key (or
ctrl-[
). Insert and replace modes are for editing text. The only
difference is that in replace mode, characters under cursor are replaced
while in insert mode
they are shifted right. colon commands
mode
(called ex command mode
in Vim
) is a special mode you can enter from
command mode
using :
key. Current mode is always printed in the
lower left corner of the screen. -
means command mode
, I
means
insert mode
, R
means replace mode
.
Moving the cursor
After starting vi, you are in command mode
. This means you can’t input
text but (among other things) you can move your cursor. There are many
ways you can move in vi
but the basic one is moving one character in
each direction. You can do this in at least two ways - using arrow keys,
which is not preferred as in order to do this, you have to move your
fingers out of keyboard home row, or using h
, j
, k
, l
keys. I
strongly recommend using hjkl
keys as much as possible.
Another argument against using arrow keys is that since busybox vi
is
used mostly on embedded devices, chances are your terminal is not
configured correctly to use them. In this situation you will be very
annoyed each time you press an arrow key.
To move the cursor to left or right, you can also use SPACE
and
BACKSPACE
keys. Note that if you are in insert mode, you can also move
cursor but hjkl
keys will not be available (because they are needed for
entering text, of course).
Basic editing
In order to type some text, you have to enter insert or replace mode.
There are many commands that enters one of those modes but the most
basic ones are a
(for appending after the current cursor position),
i
(for insert before current cursor position) and R
for entering
replace mode. After inserting some text you can use ESC
key to go back
to command mode. When in insert/replace mode, you can also delete some text
using DEL
and BS
keys.
Many vi newcomers enters insert mode just after starting the program
and use it for the whole editing session only leaving it when closing
the file. This way they basically change vi to mode less editor missing
many of its features. In order to use vi effectively, you should stay
in command mode as much as possible, only using insert mode when
entering text. The effectiveness boost will not be as big as in Vim but
you should still be able to feel it, especially when using .
command.
Saving file
Now you know how to move the cursor and do some basic editing. But how
to save the file and exit editor? There are couple of ways. If you are
editing some file that already has a file name, you can use ZZ
in
command mode. This means save and exit
. To only write changes but not
exit, you can use :write
colon command (just press colon, then type
write
). To quit, you can use :quit
command but vi
won’t let you do
that if you have some modifications that where not written yet. If you
want to discard the changes, add !
at the end of :quit
command to
force the quit. The write
and quit
command have shorter versions -
you can use :w
or :q
respectively. To write and quit at the same
time, use :wq
or :x
shortcut.
Editing in command mode
Command mode is not limited to moving the cursor. There are some
commands that you can use to edit text. The most basic one is delete
command. By pressing x
key, you can delete a character under
the cursor and by pressing X
, you can delete a character just before cursor.
Each time you delete something in vi
, it is saved in a temporary
buffer. You can paste the content of this buffer using p
(to paste
after the cursor) or P
(to paste before the cursor).
You can also copy a line to the buffer using yy
(or Y
) command and
using dd
command you can delete a line. Remember that deleted line
will be copied to a temporary buffer so you can pate it somewhere else
using p
or P
. This way you can cut&paste in vi
.
J
command can be used to join (concatenate) next line to the current
one. ~
command will toggle current character to upper/lower case. >>
and <<
commands can be used to shift left/right current line by a
tabulator. Using r{x}
command, you can replace current character with
{x}
.
While editing watch out not to type any numbers before commands since this may cause some unexpected (yet) results for you.
Boosting effectiveness
More ways to move around the file
If you edit a file, you will move around it quite a lot. vi
has some
useful commands that let you do this more efficiently. Let’s look at them.
Instead of moving left or right by one character, you can move by whole
word. There are couple of commands that let you do that. In order to use
them, you first have to know that for vi
there are two kinds of words.
I like to refer to them as word
and WORD
. First one is a bunch of
letters, numbers and underscores. Each character that is not part of
this set is considered a word
separator (and all such characters next
to each other are word itself). There’s also another definition - a
WORD
. Basically it’s any bunch of characters separated by a white
character. So for vi
, “that’s” consists of two words
but only one
WORD
.
Now the commands - w
command moves the cursor forward to the beginning
of next word
, e
moves it to the end of the next word
while b
command moves it to the beginning of next word
backwards. If you want
to move a WORD
, use W
, B
and E
commands instead. You may also
want to move whole paragraphs
- just use {
and }
commands.
0
command (or HOME
key) will move the cursor to the beginning of a
line while $
(or END
key) will move it to the end. Instead of moving
to the beginning of a line, you may want to move to the first non-blank
character in the current line with ^
command. +
will move the cursor
to the first non-blank character in the next line while -
will move to
the first non-blank character in the previous line.
If you press f{x}
(replacing {x}
with any character), vi will jump to
the next appearance of specified {x}
character in current line. Use
t{x}
instead of f{x}
if you wan to move one character before {x}
.
If you want to repeat last f
search, you can use ;
(to search
forward) or ,
(to search backwards). Unfortunately in busybox vi, ;
and ,
cannot be used for repeating t
command.
You can also search for patterns longer than one character. To do this,
use /{pattern}
command to search forward and ?{pattern}
to search
backwards. To repeat last search, use n
(forward) or N
(backwards)
commands. There’s also one useful find command - %
. Use it if you want
to find a matching ()
, {}
or []
brace.
To move the cursor to the first line visible on the screen, use H
(high) command. L
(low) command will move it to the last
visible line and M
(middle) will move it to the line in the
middle of the screen. Also gg
command will move the cursor to the
first line of file while G
command will move it to the last line of
the file.
Last group of navigation commands are scroll commands. If you want to
scroll full screen down use ctrl-f
(forward) or PgDn
key. In
order to scroll one full page up you can use ctrl-b
(backwards)
or PgUp
. You can also scroll half of the screen using ctrl-d
(down)
and ctrl-u
(up) or just one line using ctrl-e
(down) or ctrl-y
(up). You can also scroll the screen so that the current line is on the top
of the screen (z-
), in the middle (z.
) or at the bottom
z{any_other_character}
). 1
Repeating commands
I’ve already mentioned some commands that can be used to edit text or
move around the file. Most of those commands can be prefixed with a
number. This will repeat the command by specified count. For example, in
order to delete 10 lines, instead if hitting d
20 times, you can use
10dd
command. If you want to remove 5 characters, you can use 5x
, to
move 3 words forward, use 3w
and so on.
There’s special meaning of the count for gg
(move to the first line of
the file) and G
(move to the last line of the file) commands. If they
are prefixed with count, they both do the same - move to a specified
line of file. So if you want to move the cursor to 12th line of the file
you can either use 12gg
or 12G
.
There’s also one motion command I haven’t mentioned before (that’s
because there’s no much use of it if you don’t know how to use counts) -
it’s |
command. You can use it to move to a specified column in the
current line. So to move to 12th column, use 12|
.
The dot command
Unfortunately, the dot command (activated by pressing .
in command
mode) is not as powerful in busybox vi
as it is is Vim. Nonetheless it
can still be used to save you some typing. Dot repeats last command that
changed the text 2. That’s it. It doesn’t seem like much but since
you can do quite a lot using one commands in busybox vi
(end even more
in Vim
), it may save you some typing. For example if you just deleted
10 lines using 10dd
, you can delete 10 more lines pressing just .
.
Undo functionality
busybox vi
has very limited undo functionality. All you can do is to
revert all the changes in the current line using U
command. But you
can only do it as long as you don’t move to any other line.
Unfortunately there’s no redo, individual change undo or multilevel
undo.3
Using named registers
Unlike most other editors I know, vi
can use multiple registers for
storing copied text. This may be very useful if we want to paste
multiple text snippets in alternating places. By default, vi
uses
default register each time you copy or delete some text. Apart form
default register, vi
also has named registers. Each such register is
assigned to a lowercase letter (a-z
)4. You can choose which named
register should be used by prepending copy, delete or paste commands
with "{letter}
prefix.
As an example, you can copy a line to a
buffer with "ayy
, then copy
another line to b
register with "byy
. Now, whenever you want to
paste the line from a
register you use "ap
and whenever you want to
paste the line from b
register, you use "bp
.
Using marks
Many times, when editing or just viewing some long text, you will be
jumping around different parts of it. vi
can make that easier by
remembering such places as marks
. Each mark, similarly to named
registers, is assigned to a lowercase letter5. To set a mark, you use
m{letter}
command. To jump to a specific mark, you use '{letter}
command. It won’t jump to the exact place the mark was set - instead it
will jump to the beginning of the line with a mark. I often use mark q
to remember some place - to set it, I use mq
to jump to it, I use
'q
.
Note that in vi
, marks point to a specific place in a file in the time
when they where set. If text is edited, the mark may not point to the
same place again. Technically, marks points to a specific byte in file.6
Operators
Operator is a command that can be followed by a motion
in order to
apply some operation on a specified region. What is a motion? It’s
almost any command that moves cursor, like w
, b
, j
, f
, etc. Note
that for some reason, G
and gg
motions does not work in busybox
vi
.
vi
does not have many operators, just c
, d
, y
, <
, and >
.
Let’s look at them one by one:
c
is (change). It will delete specified region and change to insert mode so that you can write new text in place of the deleted one. I use it a lot in the form ofcw
(change to the beginning of next word) or to replace part of the word orc$
to replace text from the cursor to the end of line.d
is (delete). It’s similar toc
in that it deletes text but it does not change mode to insert. You can delete whole text from the cursor to the end of line usingd$
. There’s a shortcut for this, however -D
command.y
is (yank). It means copy. You can select region that should be copied to a register. To copy from the cursor to the end of word, useyw
.>
is shift right while<
is shift left. They add a tabulator at the end of each line in the region. This means that there is not much sense in using motions that don’t span multiple lines with this operators.
Each operator in vi
can be prefixed with a counter. So if you want to
shift 3 lines right, you can use 2>j
(current and two next lines).7
Remember dd
, yy
, <<
and >>
commands mentioned earlier? They are
special flavours of the d
, y
, <
and >
operators. Each repeated
operator works on the whole current line, no matter what is the current
position of the cursor in the line. So there is cc
too - it changes
whole line.8
Operators can be very powerful if you use them with f
, t
and %
motions. Also remember that you can repeat last operator using .
command.
More commands that enter insert mode
We already know some ways to enter insert mode but there are couple
more. A
is a very useful command that first jumps to the end of line
and then enters insert mode. It’s a shortcut for $a
. There’s similar
shortcut for c$
- it’s C
. It deletes from the cursor position to the
and of line and then enters insert mode. And for ^i
, there’s I
. s
is very useful - it’s a one character change - similar to c
but it’s
not an operator so you don’t have to specify the motion (but you can
still prefix it with count to change more characters). Finally, there’s
o
and O
. They both insert a new line and enters insert mode. First
of them insert line after the cursor while the second one does it before
the cursor.
Colon commands
If you press :
command, you will enter colon command submode. Your
cursor will move to the last line (status line) and you will be able to
enter colon commands. busybox vi
has only few such commands and I
already introduced some of them (:write
and :quit
).
All colon commands can be prefixed with an optional range specifier. It
consists of two parts separated by semicolon in the form of
:[from][,to]{command}
. You can omit both from
and to
or only to
.
Default value for both arguments vary between specific commands. Both
from
and to
specify range of lines the command should be applied to.
You can use line numbers, mark registers (for example :'a,'b{command}
will run command from line wit mark a
to line with mark b
), find
some text (starting from the current cursor position) with
:/text1/,/text2/{command}
or use .
(current line) and $
(last line
in the file).9 You can also mix those specifiers. For example to delete
lines from mark q
to the first occurrence of text include
, you can
use :'q,/include/d
command. You can also use %
instead of from,to
par - it’s a synonym for 1,$
which means from 1st to last line. While
you can specify range for all commands, it’s ignored by some of them
(when this doesn’t make sense).
Note that command names can be abbreviated to only few first characters.
So instead of typing :write
, you can type :wri
or even just :w
. In
case of an ambiguity the first command tested in the source code will be
used. So s
is for set
, not substitute command and f
is for file
not features
.
-
{number}
- go to line number{number}
. -
!{command}
- run command in external shell, showing its output and exit status. 10 -
=
- print current line number. -
version
- print version information. -
edit [path]
- if run without argument, reloads current file (useful if the file was modified externally. You can also specify a path to a file that will be edited instead of a current file. -
file [path]
- if run without arguments, prints current file name and status information. If you specify a path, it will be considered current file name when writing buffer to a file. -
features
- print features list11. -
list
- prints current line in the status line, showing all non-printable characters. -
quit
- just exitvi
. -
read {path}
- read file at {path} (required) and insert it just after the cursor. -
set
- if run without arguments, shows the list of run time options (and their values). Otherwise, it sets option specified as an argument. 12 -
write [path]
- if run without arguments, writes changes to the current file. If run with a path, it will write current file to specified file path. In that case you can specify range to specify which lines should be written to that file. -
delete
- delete specified (current by default) lines (copying them to the buffer). -
yank
- yank (copy) specified (current by default lines to the buffer. -
s/{find}/{replace}/[flag]
(substitute) - finds{find}
and replaces it with{replace}
. You can specify optionalg
(global) flag to find all occurrences in the line, otherwise only first will be substituted. By default it only works in current line but you can overwrite this using range specifier. 13 -
wq
/x
- shortcut for:write
and:quit
. 14 -
wn
- shortcut for:write
and:next
. -
next
,prev
(added quite recently) andrewind
are explained in the next section.
edit
, quit
, next
, prev
and rewind
commands can not be run if
there are some unsaved changes in the buffer. You can force them, however,
appending !
(without space) at the end of the command name.
Editing multiple files
If you run busybox vi
with multiple file name arguments, vi
will run
in multiple files edit mode. First change that you notice is that you
can’t quit (without forcing it) and you get “X more file(s) to edit”. In
this mode, vi
won’t let you quit until you edit (or at least take a
look at) all the files.
You can use :next
and (in recent versions of busybox) :prev
colon
commands to move to the next or previous file on the list. You can
always back to the first file using :rewind
command (it’s the only way
to get back on the beginning of the file list if your busybox vi
does
not support :prev
command).
Note that when using :edit
command, you will start editing new file.
If you move to another file, however, you won’t get back to this file.
You will always move to the files specified in the command line and there
is no way to change this list on run time in busybox vi
. 15
Run time options
Each run time option can be set using :set {option}
command. Note that
in case of a boolean options (all options except tabstop
in busybox
vi
) you can disable them prefixing them with no
. So in order to
enable autoindex
option, you should use :set autoindex
. If you want
to disable it, use :set noautoindex
. 16 Remember you can always
check all the current values using :set
command (without arguments).
Current busybox vi
supports following options (settable with :set
):
-
autoindent
(ai
) - when inserting new line, use the same amount of indent as the previous line at the beginning of the line. It’s very useful to disable this option when pasting some text using mouse clipboard, for example. -
flash
(fl
) - the screen will flash instead of beeping on error -
ignorecase
(ic
) - be case insensitive when searching -
showmatch
(sm
) - It is supposed to move your cursor to the matching[
,{
,(
opening brace when you insert closing brace. Unfortunately, this does not seem to work correctly for me. In addition, it will flash or beep, (depending onflash
option) if it can’t find a match. -
tabstop=X
- sets amount of spaces used by tabulator
Instead of using full option name you can use a shortcut specified in
braces. Note that tabstop
does not have such shortcut.
Default values for the options are hard coded in the source code. You can
change them in two ways - using -c
command line option when running
busybox vi
, or using EXINIT
environment variable. But that will only
allow you to change one one option.
Command line options and EXINIT
busybox vi
does not have many command line options. You can use -H
to
see the features list, -R
to open file in read-only mode, and -c
to
specify colon command that should be run. If you didn’t specify -c
command, you can also use EXINIT
variable to do the same.
Note that bot -c
and EXINIT
are limited to running only one colon
command. You don’t need to use colon at the beginning of the command to
run. So in order to run :version
command just after starting vi
, you
can run it like this:
busybox vi -c "version" /tmp/file
Some other useful commands:
ctrl-g
forces refreshing status linectrl-r
andctrl-l
forces screen redraw. Can be handy when you console gets filled with kernel messages, for example.ctrl-h
,ctrl-l
andctrl-j
can be used instead ofh
,l
andj
. Note that there is noctrl-k
.ctrl-m
orENTER
can be used instead of+
Conclusion
While busybox vi
is not even close to be as feature-rich as original
vi
, not to mention Vim
, I find it pretty impressive how much you
can do with it, especially since it’s just about 100KB of code. And since
it’s just few more kilobytes of compiled binary, it’s almost everywhere
busybox is. This makes it lifesaver if you want to edit something
directly on the target device.
-
Note that those commands are different in
Vim
.zt
scrolls the screen so that current line is on top,zb
scrolls it so that current line is on bottom andzz
scrolls it so that current line is in the middle. ↩ -
In Vim, dot can repeat all changing commands, even if they entered insert/repeat mode while changing the text. ↩
-
In Vim you can undo single change with
u
and redo withctrl-r
.U
can be used even if you move to the other line and sinceU
is also considered a change, you can undo it with anotherU
or using normalu
. ↩ -
Vim has more named registers - it can also use numbers to name registers. Also, you can use uppercase letters if you want to append to a register instead of replacing its content. There are also some special named registers. ↩
-
In Vim, you can also use uppercase letters for marks but they have slightly different meaning when editing multiple files. ↩
-
Vim is much smarter than that. If you set your mark to a specific line and then edit the file, it will always jump to the same line. ↩
-
In Vim you can prefix both operator and motion. The counters will multiply. So you can shift 5 lines with
2>2j
,4>j
or>4j
. That’s not supported inbusybox vi
. ↩ -
There’s a little hack in
busybox vi
implementation of repeated operator. You don’t have to repeat the same operator - just use any operator (other that shift operator) as a second character and it will treat it like it was repeated. Sody
anddc
, is the same asdd
. ↩ -
Unfortunately
busybox vi
does not support+
and-
in ranges and has no global command. ↩ -
This command uses
system()
function. Watch out for this on android as by default it will try running/bin/sh
which may not exist. Also note that there is no way to filter range of file through external command (range is just ignored),!
argument andsilent
prefix is not supported. ↩ -
There is no such command in Vim. ↩
-
busybox vi
has a very limited number of options compared to Vim. ↩ -
Substitute command is much more versatile in Vim. It supports more flags and regexp features. ↩
-
In Vim,
x
andwq
are not technically identical sincex
only writes to a file if buffer was modified. That’s not true inbusybox vi
. ↩ -
You can do this using
:args
command in Vim. ↩ -
Note that flopping boolean options with
!
suffix does not work inbusybox vi
. ↩