Busybox vi tutorial

Introduction

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.

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

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 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.

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 list.11
  • list - 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.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 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. 13
  • wq/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.

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.

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. 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 on flash 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 line
  • ctrl-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 +

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.

  1. 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 and zz scrolls it so that current line is in the middle.
  2. In Vim, dot can repeat all changing commands, even if they entered insert/repeat mode while changing the text.
  3. In Vim you can undo single change with 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.
  4. 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.
  5. In Vim, you can also use uppercase letters for marks but they have slightly different meaning when editing multiple files.
  6. 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.
  7. 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 in busybox vi.
  8. 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. So dy and dc, is the same as dd.
  9. Unfortunately busybox vi does not support + and - in ranges and has no global command.
  10. 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 and silent prefix is not supported.
  11. In Vim, x and wq are not technically identical since x only writes to a file if buffer was modified. That’s not true in busybox vi. ↩
  12. You can do this using :args command in Vim.

copied from http://k.japko.eu/busybox-vi-tutorial.html

Previous Post Next Post