You can either hate vi editor or love it. It’s usually hated by the people who don’t understand how to switch between the normal, insert an command modes and how to exit vi/vim. vim is just short from “vi IMproved” and can show you nice syntax highlighting and bunch of more features compared to vi. While many of the instructions below should work on both vi and vim, some advanced ones are definitely vim-only. So, if it does not work in vi, consider using vim.
Always make sure that caps lock is OFF and you know your keyboard layout.
Opening files with vi in command-line
To open a file in specific line number with vi use +line number and to open file on first occurrence of a search string use +/string , to open files in tabs with vi, use -p option:
# open known_hosts file on a line 378 where you might have a conflicting key $ vi +378 .ssh/known_hosts # open .bash_history on a line with first occurrence of a string password $ vi +/password .bash_history # open 3 files $ vi file1 file2 file3 # open 3 files in tabs $ vi -p file1 file2 file3 # open all files with yaml extension from current directory $ vi *.yaml # just open vi without any file $ vi
Once you can understand the modes you can explore some of the features, why vi/vim are the best ever command-line text editors, like repeating actions, recording and playing back macros, editing multiple files.
- NORMAL (default) mode. This is the mode where you are when vi starts. In this mode you can move cursor around, you can search strings, delete things and you can go from normal mode into insert mode and command mode.
- INSERT mode. This is the mode you can manually add new stuff to the file. You can go back to normal mode by pressing escape. Easiest example is that press “i” to go to insert mode, type in something and press escape to go back.
- COMMAND mode. In this mode you can save, exit, search&replace etc. You can go to command mode by pressing colon “:” while you are in normal mode. You can go back to normal mode by pressing escape. Easiest example is that press “:” to go to command mode, type in “q!” (q=quit and !=force) and press enter to exit vi without saving any changes. Typing “:qw” and enter would mean “quit and save”.
If you read and understand these few lines above about modes you should be able to open files with vi, change file and exit. Basic stuff but important to understand before going any further.
Few important notes
IMHO most important button of the vi is . (dot). It repeats last thing you did. If your last action was deleting a line, pressing . will delete another line.
You can use numbers to do something multiple times by prefixing a command with numbers. Example if your last action was deleting a line, pressing 5. (5 followed by .) will delete 5 more lines.
Commands are case-sensitive! Remember caps lock? If vi acting weird and does not do what you want, you have your caps lock on.
Word is a string from cursor up to the next space or punctuation. As an example, IP address consists of 7 words. Always.
Moving around in vi in the normal mode
Moving around in file opened in vi takes some practice. Cursor keys are not the only ones and are really slow. Fastest way to move in any direction would be to search the string and your cursor will be dropped almost instantly to the right place, at least if to compare with scrolling. Next fastest way for vertical scrolling would be CTRL-f and CTRL+b while horizontally jumping by word with w/b/W/B will be definitely faster than just scrolling manually 1 character/position at the time.
|up-arrow or k||go to the previous line, up|
|down-arrow or j||go to the next line, down|
|left-arrow or h||go one character left|
|right-arrow or l (lowercase L)||go one character right|
|1G||go to the FIRST line of the file|
|50G||go to the line 50|
|G||go to the LAST line of the file|
|$||go to the end of line|
|0 (zero) or ^||go to the beginning of line|
|w,W||go to the beginning of next word, to the right. W skips one punctuation or space.|
|b,B||go to the beginning of previous word, to the left. B skips one punctuation or space.|
|e||go to the end of the word|
|CTRL+f||go to one screen/page forward or scroll down.|
|CTRL+b||go to one screen/page backwards or scroll up.|
|CTRL+d||go to half the screen/page forward or scroll down.|
|CTRL+u||go to half the screen/page backwards or scroll up.|
|H||go to the upper left corner of the screen|
|M||go to the beginning of the line in the middle of the screen|
|L||go to the lower left corner of the screen|
|z+ENTER||reposition window so that cursor is in the top|
|z-||reposition window so that cursor is in the bottom|
|z.||reposition window so that cursor is in the middle|
|gt, gT||Switch to next tab, switch to previous tab|
|CTRL+w followed by k||Switch to upper window when screen has been split|
|CTRL+w followed by j||Switch to lower window when screen has been split|
|CTRL+w followed by h||Switch to window in the left when screen has been split|
|CTRL+w followed by l||Switch to window in the right when screen has been split|
Undo and redo in vi in normal mode
If you are used to fire CTRL+z to undo changes, then its useful to know that it will not work in vi. Instead it stops vi process and backgrounds it. You can get it back by running command fg in your shell.
|CTRL+r||redo – undo last thing you undid with pressing u. Re-doing just the last thing you undid can be done also with . (dot).|
|U||roll back all changes you did on the last edited LINE|
Entering insert mode for inserting, replacing & changing text
All the commands below should be fired in NORMAL mode and they take you to INSERT mode, press escape to finish and go back to normal mode.
|i||insert text to the left of cursor.|
|I (capital i )||Insert text to the beginning of line.|
|a||Append text to the right of cursor.|
|A||Append text to the end of line|
|r||Overwrite single character under the cursor. Goes back to normal mode after you input single character.|
|R||Overwrite text from cursor to the right|
|C, c$||Replace text from cursor to the end of line|
|3C||Replace text from cursor to the end third newline. Current line and 2 lines below.|
|cw||Change Word – replaces word|
|7cw||Change 7 Words – replace 7 words like IP address|
|o||Add new line after current line|
|O||Add new line before current line|
|s||substitute character under cursor with one or more characters. Like r and i mixed.|
|S, cc||substitute entire line. Clears the line, otherwise same as pressing C in the beginning of the line.|
Deleting / truncating / changing / joining in normal mode
You don’t need to go to the insert mode and start deleting text with backspace or delete. There are faster ways described in table below. You run those commands in normal mode and you remain in normal mode (no need for pressing escape).
|x||Delete character under cursor|
|X||Delete character after the cursor|
|5x||Delete 5 characters – the one under cursor and 4 which follow.|
|dd||Delete line under cursor|
|100dd||Delete 100 lines – line under cursor and 99 lines below. Also useful for truncating short files, might be easier to remember than other ways.|
|dw||Delete word -delete from under the cursor to the end of the word|
|7dw||Delete 7 words like IP address|
|db||Delete word before the cursor|
|D||Delete from cursor to the end of line|
|3D||Delete from cursor to the end of third newline. Current line and two lines below.|
|dG||Delete from current line to the end of file. It is easiest way for truncating entire file if you happen to be on the first line with your cursor. if not, then press 1G to go there.|
|CTRL+a||increase number under cursor by one. if cursor is not on a number, then increases first number after cursor by one|
|CTRL+x||decrease number under cursor by one. if cursor is not on a number, then decreases first number after cursor by one|
|J||Moves cursor to the end of line and joins next line with current one. Extremely handy to replace newlines with space.|
Copy & paste in vi 1/3 – general stuff
If you have copied something from some other application or from vi screen to clipboard, then you need to go to insert mode before pasting the clipboard. Otherwise your pasted string is interpreted as line of commands right up to first command which takes you into insert mode. Keys to copy to and from clipboard depend on your application/terminal config. Linux default combinations are CTRL+insert for copy and SHIFT+insert for paste.
Copy inside vim is YANK – this has nothing to do with clipboard. Yanked contents will be stored by default in vi general buffer.
|yy or Y||yank line|
|20yy or 20Y||yank 20 lines – current line and 19 below|
|7yw||yank 7 words like ip address|
|p||put/paste yanked contents after cursor. If you yanked words, they will be pasted after cursor. if you yanked a line, it will be pasted after current line.|
|P||put/paste yanked contents before cursor. If you yanked words, they will be pasted before cursor. if you yanked a line, it will be pasted before current line.|
|100p||put/paste yanked content 100 times. If you yanked a line, it will make 100 copies of it.|
Copy & paste in vi 2/3 – moving lines and words with delete?!!
Everything you delete are yanked – deleting something with d/D/x/X commands will put deleted contents into general yank buffer so you can just use p to undo/restore. That allows you to move words or lines like so:
To move 3 lines press 3dd which deletes 3 lines (and yanks them). Move to the new location where you want these 3 lines and press p to put them after current line or P to put them before current line. That’s it! No need to yank something paste them to new location and then going back to original location to delete them.
Copy & paste in vi 3/3 – The Buffers
Vi has ability to store yanked text to internal buffer named a to z. These buffers could be compared with multiple items in the clipboard but there is also option to append something to already existing buffer – you can put things together in one or more buffers and then just go and put/paste somewhere, like new file. You don’t get any visual feedback, so vi buffers are like D&D in your head – to slay a dragon, one must start with “ (double quotes). Using uppercase letters as buffer names will append to the buffer.
|“a10dd||delete 10 lines and add them to buffer a|
|“ap||paste buffer a contents below current line|
|“b5yy||yank 5 lines to buffer b|
|“Byy||yank line and append it to buffer b|
|“bP||put/paste buffer b contents above current line|
|“c4yw||yank 4 words to buffer c|
|“C3yw||yank 3 words and append to buffer c (appending adds newline)|
Buffers remain in memory until you close the vi. Its useful to copy paste data from one file to another using buffers. Open one or more files, copy parts of it to buffers, open or just switch to next file and paste the buffers.
Searching in vi
Use / or ? to search strings. Press enter to begin searching.
|n||forward search, FIND NEXT|
|N||backward search, FIND PREVIOUS|
|fx & ;||find/jump to first occurrence of character x in current line. press ; semicolon to find next x. Search scope is very limited, only line where your cursor is and after, right side of the cursor.|
You can do semi-automated search & replace if you search something, then replace it and after that just press n and . Extremely handy to replace similar parameters like IP addresses or host names in one or more files without using search&replace command in command mode which does not let you preview the results. Note that if you are modifying more than one file at the time, you can jump to next file and just press n to search last thing.
You can go to command mode from normal mode by pressing : (colon). After that enter your command and press enter.
Command mode 1/3 – general stuff
|:w||write, save file|
|:w!||force write file – usually needed when you want to update read-only files|
|:w newfile||write a copy of open file to “newfile”. open file remains open in vi, it does not switch to newfile.|
|:q!||quit and ignore changes|
|:wq||save and quit (same as command ZZ in normal mode)|
|:wq!||force save and quit (read above)|
|:r filename||read contents from file and place them after the line with cursor|
|close current file and open file for editing. if you opened multiple files, it closes only currently open file and leaves the rest.|
|:10 r filename||read contents from file and place them in currently open file after 10th line|
|:1,16w new.sh||write lines 1 to 16 into file new.sh. If file exist and you want to truncate its previous contents use ! like :1,16w! new.sh|
|:1,16w>> new.sh||append lines 1 to 16 into file new.sh.|
|:n||open next file, useful if you opened multiple files with vi. use :n! to open next file without saving changes in current one.|
|:N or :prev||open previous file. useful if you opened multiple files with vi|
|:rew||rewind – go back to first opened file. useful if you opened multiple files with vi|
|:10||go to line 10|
|:!/bin/bash||run shell command /bin/bash . One reason why you should never grant sudo privileges to run vi or edit some file as root.|
|:tabclose||close current tab|
|:tabonly||close all other tabs except current one|
|:qa!||close all tabs and exit without saving changes|
|:wqa||close all tabs and exit with saving changes|
|:tabedit filename||open file in new tab and switch to that tab|
|:tabs||show list of tabs and open file names with paths|
|:tabedit filename |tabedit filename2||open multiple files into tabs from command mode|
|:vs or :vsplit||split screen vertically|
|:sp or :split||split screen horizontally|
Command mode 2/3 – search & replace and regular expressions
Search and replace in vi is substitute. When running substitute in command mode, you can define a scope or range where to perform it, search and replace strings and flags.
General substitute command syntax is like this
|16||line 16 only|
|1,16||range: lines 1 to 16|
|.+7||7th line under current line|
|.,.+7||range: 8 lines from current line (.) to the 7th line after current (.+7)|
|g/somestring/||only line(s) containing somestring|
It is possible to use regular expressions in search strings
|.||Any single character|
|*||Any previous character|
|^||Beginning of the line|
|$||End of the line|
Flags in the end of substitute command can be combined, you can use more than one.
|g||global – perform substitution on entire file which is currently being edited.|
|c||ask confirmation for each replace|
|I (capital i )||case sensitive search|
Example searches are below, feel free to combine any other substitutions from the scopes, regexes and flags above.
|:%s/foo/bar||should replace first occurrence of word foo with bar in currently opened file. testing showed various results like replacing several but not all foo-s. Better use the next example.|
|:%s/foo/bar/g||replace all occurrences of string foo with string bar in entire file.|
|:%s/^#//g||comment in / remove hash from beginning of all lines in open file. does not work if # has indentation in the front.|
|:%s/^ *//g||remove all spaces from the beginning of the line. will not remove tabs.|
|:%s/^\s*//g||remove all white-space from the beginning of the line. \s indicates white-space which matches both space and tab. To search tabs only use \t.|
|:%s/ *$//g||remove all spaces from the end of the line. will not remove tabs.|
|:%s/E/x/gI||replace all uppercase E characters with lowercase x characters|
|:g/foobar/foo/bar/g||replace all foo-s with bar on lines containing foobar|
Command mode 3/3 – Parameters
Its possible to set various parameters in vi/vim with set command in command mode. Most common of them is probably showing/hiding line numbers.
|:set all||Display all supported parameters and their current values if any.|
|Display/show line numbers in vi. if you want to copy it afterwards from screen to clipboard, you might get the line numbers copied too.|
|:set list||Display non-printable characters. Extremely handy to show newlines and identify whitespace in the end of line.|
|:set nolist||Hide non-printable characters|
|:set autoindent||make vi to follow indentation of a previous line when you add newline. generally pads line beginning with spaces to the lenght of what previous line had.|
|:set noautoindent||turns off indentation/padding.|
|:set showmatch||shows matching parenthesis when you type the closing parenthesis. Its not exactly a highlighting, cursor just jumps to opening bracket and back.|
|:set noshowmatch||turns off the matching bracket showing.|
|:set showmode||shows you mode in the last line of the screen when you are in insert and/or macro recording mode|
|:set noshowmode||turns off mode showing|
When . (dot) allowed you to repeat LAST action, then macros allow you to repeat many of them – a sequence of these actions what you previously recorded, any number of times. Macro recording and playback is done in normal mode. To start recording, press q in normal mode followed by one lowercase letter which is assigned to macro name for example qa. While you record you can use all the vi features&commands to do whatever you want (to repeat). Press q again to stop recording. If you want to playback the macro then press @ followed by macro name, for example @a. If you want to playback macro named a 10 times press 10@a . This very much resembles scientific calculator programming (Citizen, Электро́ника МК-61, MK-52 etc.) – you have to think in steps.
Assuming, you have a file with 100+ lines of text and you want to add increasing number to the front of each line starting from 1 up to 100 and a space between number and text. Open file and follow steps in first column.
|1 G||go to line 1 – optional, you are probably in the first line already|
|I (capital i)||Insert into the beginning of line|
|1 followed by space and esc||add “1 ” to the beginning of first line, go back to normal mode|
|now you should have first line with prefix 1 done. make sure you are in first line before starting macro recording|
|q a||record macro with name a|
|0 y w||go to beginning of line, yank word (line number)|
|j 0 P||go down one line, go to beginning of the line and paste word in the front of the cursor (line number on previous line)|
|0 CTRL+a||go to beginning of the line and increase number under the cursor by 1|
|by now you should have two numbered lines. make sure you are on second line before running a macro… or you might just get list to 99|
|99@a||run macro a 99 times|
CSV file into SQL with vi
Assuming that you have csv file with 4 fields like this:
And you want to convert it to SQL insert statements with incrementing primary key like this:
insert into table1 values (1, “abc”,”abc”,”abc”,”abc”);
insert into table1 values (2,”abc”,”abc”,”abc”,”abc”);
insert into table1 values (3,”abc”,”abc”,”abc”,”abc”);
Open file and follow steps in first column.
|1 G||go to line 1 – optional, you are probably in the first line already|
|I (capital i)||Insert into the beginning of line|
|insert into table1 values (1, ESC||type in the first part and go back to normal mode with escape|
|A ) ; ESC||append ); to the end of line and go back to normal mode with escape|
|you should have now converted your first line to SQL. make sure you are on first line before starting to record|
|q a||record macro with name a|
|0 7 y w||go to beginning of the line and yank 7 words|
|j 0 P||go down one line, go to beginning of the line and paste word in the front of the cursor|
|A ) ; ESC||go to the end of line and add );|
|0 5 w CTRL+a||go to the beginning of the line, jump to the 5th word and increase it by one|
|by now you should have two lines with sql syntax. make sure you are on second line before running a macro|
|N@a||run macro “a” N times. replace N with the number of lines you want to convert.|
Various other commands
Old Estonian proverb: “There are 3 things which are certain in life: death, taxes and there are always more vi commands than you know.”
|CTRL+g||display file name, number of lines, current line and column number where the cursor is|
|CTRL+l (lowercase L)||refresh the screen, might be handy if there are some glitches|
|CTRL+h||move backwards 1 character in INSERT MODE|
|CTRL+w||move backwards 1 word in INSERT MODE|
|CTRL+u||move to the beginning of insert in INSERT MODE|
Indentation in vim
I personally like indentation with spaces instead of tabs and indentation width 2 spaces so there is an example configuration below how to do that. To make it easier, I add those commands into /etc/vimrc to make it global or into ~/.vimrc to apply it only to my user, that saves me from typing those commands in all the time. Also it seems that automatic indentation is pretty useless if you have multiple commands on the same line.
set expandtab set shiftwidth=2 set softtabstop=2 filetype plugin indent on
- expandtab – uses spaces instead of tabs.
- shiftwidth=2 – sets indentation to 2 spaces
- softtabstop=2 – sets how many spaces you get when you press tab
- filetype plugin indent on – enables automatic indentation
|==||automatically indent current line|
|gg=G||Go to first line, and auto-indent file until the end of file. of course you can omit the gg and indent from any line where your cursor is, until to the end of file|
|5==||indent 5 lines automatically|
|>>||indent current line by one indent|
|<<||remove one indent from current line|
|5>>||indent 5 lines by one indent|
|5<<||remove one indent from 5 lines|
If you found this useful, say thanks, click on some banners or donate, I can always use some beer money.