---
title: 'Busybox vi tutorial'
url: 'https://willifix.net/blog/busybox-vi-tutorial'
markdown: 'https://willifix.net/blog/busybox-vi-tutorial.md'
date: '2024-06-05'
description: '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 implementa…'
---

# Busybox vi tutorial

## [Busybox vi tutorial](https://willifix.net/blog/busybox-vi-tutorial)

    5th Jun 2024  

### 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 `: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.

### 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](#note1)

### 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](#note2). 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](#note3)

### 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](#note4) 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](#note5) 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](#note6)

### 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](#note7)

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](#note8)

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](#note9) 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](#note10)
- `=` - 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](#note11)
- `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](#note12)
- `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:

```shell
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](https://willifix.net/blog/nextjs-with-supabase-login-problem) [Next Post ](https://willifix.net/blog/howto-check-charging-watt-income-under-linux)

---

## Navigation

- Parent: [Blog](https://willifix.net/blog.md)
- Previous: [howto check Charging (Watt income) under Linux](https://willifix.net/blog/howto-check-charging-watt-income-under-linux.md)
- Next: [nextjs with supabase login problem](https://willifix.net/blog/nextjs-with-supabase-login-problem.md)
