I often use shells to check the content of running docker containers (docker exec -it {container} /bin/sh). Often we only have Busybox
in the containers. To Master the work in running containers it is good to have a Documentation of the included vi
.
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.
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
.
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
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
.
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).
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.
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 :qui
t 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.
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.
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
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|
.
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 .
.
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
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
.
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 letter.5 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
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 of cw
(change to the beginning of next word) or to replace part of the word or c$ to replace text from the cursor to the end of line.d
is (delete). It’s similar to c
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 using d$
. 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, use yw
.>
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.
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.
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 list.11list
- prints current line in the status line, showing all non-printable characters.quit
- just exit vi.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.12write [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 optional g (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. 13wq/x
- shortcut for :write
and :quit
.wn
- shortcut for :write
and :next
.next
, prev
(added quite recently) and rewind
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.
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
.
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
. 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 errorignorecase
(ic
) - be case insensitive when searchingshowmatch
(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 on flash
option) if it can’t find a match.tabstop=X
- sets amount of spaces used by tabulatorInstead 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.
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
and ctrl-l
forces screen redraw. Can be handy when you console gets filled with kernel messages, for example.ctrl-h
, ctrl-l
and ctrl-j
can be used instead of h
, l
and j
. Note that there is no ctrl-k
.ctrl-m
or ENTER
can be used instead of +
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.
Vim
. zt
scrolls the screen so that current line is on top, zb
scrolls it so that current line is on bottom and zz
scrolls it so that current line is in the middle.
u
and redo with ctrl-r
. U
can be used even if you move to the other line and since U
is also considered a change, you can undo it with another U
or using normal u
.
2>2j
, 4>j
or >4j
. That’s not supported in busybox vi
.
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. So dy
and dc
, is the same as dd
.busybox vi
does not support +
and -
in ranges and has no global command.
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 and silent
prefix is not supported.
copied from http://k.japko.eu/busybox-vi-tutorial.html