VI Power User - Mastery and Efficiency
Welcome to VI Mastery
You've learned the commands, the techniques, and the configurations. Now it's time to learn the secrets that separate power users from experts. These are the tricks that make VI users legendary for their speed and efficiency.
The Power User Mindset:
A VI master thinks in terms of text objects, motions, and transformations. They compose commands like sentences, automate repetitive tasks ruthlessly, and never waste a keystroke. This tutorial will teach you to think like a VI master.
Keystroke Efficiency - Every Character Counts
True power users minimize keystrokes. Here's how:
Single-Key Powerhouses
| Instead of |
Use |
What It Does |
:wq |
ZZ |
Save and quit (2 keys vs 4) |
:q! |
ZQ |
Quit without saving (2 vs 4) |
dd then p |
ddp |
Swap lines (3 keys vs 4) |
yy then p |
yyp |
Duplicate line (3 vs 4) |
cc |
S |
Change line (1 vs 2) |
d$ |
D |
Delete to end of line (1 vs 2) |
c$ |
C |
Change to end of line (1 vs 2) |
y$ |
Y |
Yank to end (1 vs 2) |
Motion Shortcuts
# Instead of multiple h/l movements:
f char # Find next char on line (one key!)
F char # Find previous char
t char # To before char
T char # Back to before char
; # Repeat f/F/t/T
, # Repeat in opposite direction
# Instead of multiple w movements:
W # Skip to next whitespace-delimited word
E # End of whitespace-delimited word
# Instead of scrolling:
H # Jump to High (top of screen)
M # Jump to Middle
L # Jump to Low (bottom)
The Power of Counts
# Combine counts with any command
3fa # Find 3rd 'a' on line
2W # Skip 2 words forward
5dd # Delete 5 lines
10p # Paste 10 times
8j # Down 8 lines
4>> # Indent 4 lines
EFFICIENCY RULE: If you're about to press the same key more than twice, there's probably a better way. Think count + motion instead of repeating single movements.
Advanced Navigation Patterns
Sentence and Paragraph Objects
( # Previous sentence
) # Next sentence
{ # Previous paragraph
} # Next paragraph
# Combined with operators:
d) # Delete to end of sentence
c} # Change to end of paragraph
y( # Yank from start of sentence
v{ # Visual select to start of paragraph
Section Navigation
[[ # Previous section (or function)
]] # Next section
[] # Previous section end
][ # Next section end
# In code files:
[{ # Previous unmatched {
]} # Next unmatched }
[/ # Previous unmatched /* comment
]/ # Next unmatched */ comment
Jump List Mastery
Ctrl+o # Jump to older position in jump list
Ctrl+i # Jump to newer position
:jumps # View jump list
:clearjumps # Clear jump list
# Strategic jumping:
G # Creates jump - can return with Ctrl+o
/pattern # Creates jump
'a # Mark doesn't create jump
`a # Mark doesn't create jump
JUMP STRATEGY: Large movements (G, /pattern, line numbers) create jumps. Use Ctrl+o to return. Small movements (hjkl, w, b) don't create jumps. Plan your navigation accordingly!
Compound Command Patterns
Master VI users compose complex edits with simple command combinations.
The ci/di Family
# Change/Delete inside paired characters
ci" # Change inside quotes
ci' # Change inside single quotes
ci` # Change inside backticks
ci( # Change inside parentheses
ci[ # Change inside brackets
ci{ # Change inside braces
ci< # Change inside angle brackets
cit # Change inside HTML/XML tags
# The 'a' (around) variants include delimiters
da" # Delete quotes and contents
da( # Delete parentheses and contents
dat # Delete tag and contents
Multi-Step Edits in One Flow
# Delete function and paste at end:
d]]G p
# Copy section and insert at beginning:
y}gg P
# Change word and repeat elsewhere:
ciw newtext ESC n .
# Delete to mark and move to another position:
d'a 'b p
# Yank paragraph, go to end, paste 3 times:
yap G 3p
The "Do Then Dot" Pattern
# Make a change once, repeat with .
cw newname ESC # Change word
n . # Find next, repeat change
n . # Again
n . # Again
# Delete and move pattern:
dd # Delete line
5j # Move down
. # Repeat delete (deletes current line)
3j # Move
. # Repeat
# Format code blocks:
>i{ # Indent block
]] # Next function
. # Repeat indent
MASTERY PRINCIPLE: Design your first edit to be repeatable with dot. The dot command repeats your last change, not your last motion. Use this to your advantage!
Ex Scripting Power
Ex commands can be scripted and chained for incredible power.
Chaining Ex Commands
# Use | to chain commands
:%s/old/new/g | %s/foo/bar/g
# Update multiple files:
:args *.txt | argdo %s/old/new/ge | update
# Complex text transformation:
:g/pattern/d | %s/old/new/g | sort
# Process and save:
:%!sort | uniq | w sorted.txt
Ex Scripts in Files
Create cleanup.ex:
" Remove trailing whitespace
%s/\s\+$//e
" Remove empty lines
g/^$/d
" Sort lines
%!sort
" Remove duplicates
%!uniq
" Save and quit
wq
Execute script:
ex -s file.txt < cleanup.ex
# Or from within vi:
:source cleanup.ex
Batch Processing with Ex
# Process multiple files from command line:
ex -sc '%s/old/new/ge|x' file.txt
# Process all files in directory:
for file in *.conf; do
ex -sc '%s/old/new/ge|x' "$file"
done
# More complex:
ex -sc 'g/DEBUG/d|x' logfile.txt
# Chain multiple ex scripts:
ex -sc 'source script1.ex|source script2.ex|x' file.txt
Ex Script Library
~/scripts/remove-comments.ex
" Remove shell-style comments
g/^\s*#/d
" Remove blank lines
g/^$/d
wq
~/scripts/format-logs.ex
" Extract just errors
v/ERROR/d
" Remove timestamps
%s/^\d\{4}-\d\{2}-\d\{2} \d\{2}:\d\{2}:\d\{2} //
" Sort and count
%!sort | uniq -c | sort -rn
wq
~/scripts/add-headers.ex
" Add Python header
0i
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Module description."""
.
wq
TRY IT NOW: Create a simple ex script that removes blank lines and saves. Test it with: ex -sc 'source myscript.ex' testfile.txt
Advanced Macro Techniques
Recursive Macros
# Macro that calls itself until end of file:
qq # Start recording
(do something)
j # Move down
@q # Call itself
q # Stop recording
# Execute once - it will run until bottom:
@q
# Example: Add line numbers recursively
qq
I1. ESC
j
@q
q
Note: Recursive macros stop when they hit an error (like "j" at end of file), so they're safe to use.
Macro Editing
# View macro contents:
:reg a
# Edit macro by pasting, editing, yanking back:
"ap # Paste macro 'a' to buffer
(edit it)
"add # Delete line into register 'a'
# Or directly set:
:let @a='inew macro content^[j'
# Note: ^[ is ESC, type with Ctrl+v ESC
Building Complex Macros
EXAMPLE: Convert CSV to SQL INSERT
# Starting data:
John,Doe,john@example.com
Jane,Smith,jane@example.com
# Record macro:
qa
I INSERT INTO users VALUES ('
ESC
f,
r',
f,
r',
A');
ESC
j
q
# Result:
INSERT INTO users VALUES ('John','Doe','john@example.com');
INSERT INTO users VALUES ('Jane','Smith','jane@example.com');
Macro Libraries
Save macros in .vimrc:
" Macro to comment Python line
let @c = 'I# ^[j'
" Macro to uncomment Python line
let @u = '^3xj'
" Macro to add TODO comment
let @t = 'A # TODO(user): ^[j'
" Macro to wrap in quotes
let @q = 'i"^[A"^[j'
" Macro for function template
let @f = 'odef ():\n """Docstring."""\n pass^[kk$hi'
MACRO MASTERY: Build a personal library of macros for your common tasks. Store them in your .vimrc with descriptive register names. Memorize your top 5 most-used macros.
Buffer and Register Mastery
The Complete Register Set
| Register |
Purpose |
Usage |
" |
Unnamed (default) |
Last delete/yank |
0 |
Last yank |
Yank-only buffer |
1-9 |
Delete history |
Last 9 deletes |
a-z |
Named registers |
Your clipboards |
A-Z |
Append registers |
Append to a-z |
- |
Small delete |
Last small delete |
. |
Last inserted text |
Read-only |
% |
Current filename |
Read-only |
# |
Alternate filename |
Read-only |
: |
Last command |
Read-only |
/ |
Last search |
Read-only |
+ |
System clipboard |
OS integration |
* |
Selection clipboard |
OS integration |
Advanced Register Usage
# Use special registers in commands:
:e <C-r>% # Edit current filename
:e <C-r># # Edit alternate filename
/<C-r>/ # Reuse last search
:<C-r>: # Reuse last command
# In insert mode:
<C-r>" # Insert from unnamed register
<C-r>0 # Insert last yank
<C-r>a # Insert from register a
<C-r>% # Insert current filename
<C-r>/ # Insert last search
# Paste in command mode:
:s/<C-r>a/<C-r>b/ # Use registers in substitution
Multi-Buffer Workflows
WORKFLOW: Refactoring Code
# Collect all function definitions:
:g/^def /y A
# Collect all class definitions:
:g/^class /y B
# Collect all imports:
:g/^import /y C
:g/^from /Y C
# Create new file structure:
:e refactored.py
"Cp # Paste imports
:$r template.py # Read template
"Bp # Paste classes
"Ap # Paste functions
WORKFLOW: Documentation Generation
# Extract all function names:
:g/^def \w\+/y A
# Process them:
:e functions.txt
"ap
:%s/^def \(\w\+\).*/\1()/
:%s/^/- /
# Result is a markdown list of functions
Pattern Matching Mastery
Advanced Regex Patterns
# Lookahead and lookbehind (vim with \@=, \@!)
/foo\(bar\)\@= # foo followed by bar
/foo\(bar\)\@! # foo NOT followed by bar
/\(foo\)\@<=bar # bar preceded by foo
/\(foo\)\@<!bar # bar NOT preceded by foo
# Very magic mode (vim) - less escaping:
/\vpattern # Very magic
/\v(\w+)@(\w+)\.(\w+) # Email pattern
# Greedy vs non-greedy:
/.*foo # Greedy - matches to last foo
/.\{-}foo # Non-greedy - matches to first foo
# Complex character classes:
/\v[[:alpha:]] # Letters
/\v[[:digit:]] # Digits
/\v[[:alnum:]] # Alphanumeric
/\v[[:space:]] # Whitespace
/\v[[:punct:]] # Punctuation
Zero-Width Patterns
# Word boundaries:
/\<word\> # Exact word match
# Line boundaries:
/^start # Line starting with
/end$ # Line ending with
/^$ # Empty line
/^\s*$ # Blank line (may have spaces)
# Multi-line patterns:
/start\_.\{-}end # Across multiple lines (vim)
Capture Groups and Backreferences
# Swap words:
:%s/\(\w\+\) \(\w\+\)/\2 \1/
# Extract domain from email:
:%s/.*@\(.*\)/\1/
# Convert date format:
:%s/\(\d\{2}\)\/\(\d\{2}\)\/\(\d\{4}\)/\3-\1-\2/
# Remove duplicate words:
:%s/\(\w\+\) \1/\1/g
# Wrap in tags:
:%s/\(.*\)/
\1<\/div>/
REAL-WORLD: Parse Apache Logs
# Extract just IP, time, and status:
:%s/^\([0-9.]\+\).*\[\(.*\)\].*" \(\d\{3}\).*/\1 \2 \3/
# Extract failed requests (4xx, 5xx):
:v/" [45]\d\{2} /d
# Count IPs:
:%s/^\([0-9.]\+\).*/\1/
:%!sort | uniq -c | sort -rn
Performance and Scale
Handling Large Files
# Open large file efficiently:
vim -u NONE large.log # Skip vimrc
vim large.log
:set noswapfile # Disable swap
:set eventignore=FileType # Skip filetype detection
:syntax off # Disable syntax
:set nowrap # Don't wrap
:set lazyredraw # Faster screen updates
# Process in chunks:
:1,10000!sort # Sort first 10k lines
:10001,$!sort # Sort rest
# Use ex for batch:
ex -sc '1,10000d|x' large.log
Optimizing Macros
# Before running macro on many lines:
:set lazyredraw # Don't redraw screen
# Run macro:
1000@a # Execute 1000 times
# After:
:set nolazyredraw # Re-enable
Parallel Processing
# Process multiple files in parallel:
ls *.txt | parallel -j4 'ex -sc "%s/old/new/ge|x" {}'
# Or with xargs:
ls *.txt | xargs -P4 -I{} ex -sc '%s/old/new/ge|x' {}
# Split file and process:
split -l 10000 large.txt chunk_
for chunk in chunk_*; do
ex -sc 'g/pattern/d|x' "$chunk" &
done
wait
cat chunk_* > processed.txt
Integration and Automation
VI in Shell Scripts
Automated Config Updates:
#!/bin/bash
# update-config.sh
# Backup
cp /etc/myapp.conf /etc/myapp.conf.bak
# Update with ex:
ex -sc '%s/old_server/new_server/g|x' /etc/myapp.conf
# Restart service
systemctl restart myapp
Log Rotation Cleanup:
#!/bin/bash
# cleanup-logs.sh
for log in /var/log/app/*.log; do
# Keep only errors
ex -sc 'v/ERROR/d|x' "$log"
# Compress
gzip "$log"
done
VI from Cron
# In crontab:
0 2 * * * ex -sc 'g/old/d|x' /var/log/daily.log
# Or use ex script:
0 2 * * * ex -s /var/log/daily.log < /scripts/cleanup.ex
VI with Find
# Update all files:
find . -name "*.conf" -exec ex -sc '%s/old/new/ge|x' {} \;
# Process matching files:
find . -type f -name "*.txt" | while read f; do
ex -sc 'g/pattern/d|x' "$f"
done
VI in Git Hooks
.git/hooks/pre-commit:
#!/bin/bash
# Remove trailing whitespace before commit
git diff --cached --name-only | while read file; do
if [ -f "$file" ]; then
ex -sc '%s/\s\+$//e|x' "$file"
git add "$file"
fi
done
Power User Challenges
Test your mastery with these real-world challenges!
CHALLENGE 1: Log Analysis
Given a 10MB Apache access log:
- Extract all unique IP addresses
- Count requests per IP
- Sort by request count (highest first)
- Show top 10
- Format as table
Goal: Complete in under 10 VI commands
Show Solution
# Open file
vim access.log
# Extract IPs (first field)
:%s/^\([0-9.]\+\).*/\1/
# Sort and count
:%!sort | uniq -c | sort -rn
# Keep top 10
:11,$d
# Format as table
:%s/^ *\(\d\+\) \(.*\)/\2\t\1/
:%!column -t
# Save
:w top_ips.txt
CHALLENGE 2: Code Refactoring
Refactor Python code:
- Find all print statements
- Replace with logging.debug
- Add import at top if not present
- Format with proper logging style
Show Solution
# Replace prints
:%s/print(\(.*\))/logging.debug(\1)/g
# Check if import exists
:g/import logging/
# If not found, add at top
:1
Oimport logging
<ESC>
# Verify
:w
CHALLENGE 3: Config File Merge
Merge 5 config files, removing duplicates and sorting sections
Show Solution
# Create new file
:e merged.conf
# Read all files
:r file1.conf
:r file2.conf
:r file3.conf
:r file4.conf
:r file5.conf
# Remove duplicate lines
:%!sort -u
# Group by section (lines starting with [)
:g/^\[/normal! {d}}
# Save
:w
CHALLENGE 4: Data Transformation
Convert 1000 lines of CSV to JSON array of objects
Show Solution
# Add quotes and commas to each field
:%s/\([^,]*\)/"\1"/g
# Replace commas with JSON format
:%s/","/": "/g
# Add braces
:%s/^/ {"/
:%s/$/},/
# Add array brackets
:1i
[
.
:$a
]
.
# Fix last object comma
:$-1s/,$/
# Save
:w data.json
The VI Master's Toolkit
Essential Aliases and Functions
Add to ~/.bashrc:
# Quick VI operations
alias vir='vi -R' # Read-only
alias vis='vi -S' # Load session
alias vic='vi -c' # Run command on open
# Ex batch operations
vix() {
ex -sc "$1|x" "$2"
}
# Example: vix 'g/pattern/d' file.txt
# Clean all files in directory
viclean() {
for f in "$@"; do
ex -sc '%s/\s\+$//e|g/^$/d|x' "$f"
done
}
# Add header to files
viheader() {
local header="$1"
shift
for f in "$@"; do
ex -sc "0i\n$header\n.\nwq" "$f"
done
}
Power User .vimrc Additions
" Quick file operations
nnoremap <leader>cd :cd %:p:h<CR> " CD to current file's directory
nnoremap <leader>cf :let @+=@%<CR> " Copy filename to clipboard
" Smart pasting
nnoremap <leader>p :set paste<CR>"+p:set nopaste<CR>
" Quick substitution
nnoremap <leader>s :%s//g<Left><Left>
vnoremap <leader>s :s//g<Left><Left>
" Execute current line
nnoremap <leader>e :.w !sh<CR>
" Format JSON
nnoremap <leader>j :%!python -m json.tool<CR>
" Format XML
nnoremap <leader>x :%!xmllint --format -<CR>
" Sort selection
vnoremap <leader>s :!sort<CR>
" Remove duplicates
nnoremap <leader>u :%!sort -u<CR>
" Count occurrences of word under cursor
nnoremap <leader>* :%s/<C-r><C-w>//gn<CR>
" Open URL under cursor
nnoremap gx :!open <cfile><CR>
Debugging and Profiling
Debug VI Behavior
# Verbose mode:
vim -V9logfile # Verbosity level 9, log to file
:set verbose=9 # Set verbosity in session
# Debug key mappings:
:verbose map <key> # Show where mapping was defined
# Debug options:
:verbose set option? # Show where option was set
# Check what's slowing down:
:profile start profile.log
:profile func *
:profile file *
(do slow operation)
:profile pause
:qa!
Startup Time Profiling
# Time vim startup:
vim --startuptime startup.log
# Analyze the log:
sort -k2 -n startup.log | tail -20
# Find slow plugins:
# Disable plugins one by one:
vim --noplugin
The Path to Mastery
THE POWER USER'S JOURNEY:
- Week 1-2: Use VI exclusively. Resist the urge to use other editors.
- Week 3-4: Challenge yourself to reduce keystrokes. Count them.
- Month 2: Build your macro library. Record everything you do twice.
- Month 3: Master one text object type per week (quotes, brackets, tags).
- Month 4: Learn one new ex command per day. Use it that day.
- Month 5: Optimize your .vimrc. Map your most common patterns.
- Month 6: Teach someone else. Teaching reveals gaps in understanding.
DAILY PRACTICE:
- Morning: Review one section of this tutorial
- During work: Apply one new technique
- Evening: Record a macro for tomorrow's repetitive task
- Weekly: Time yourself on a challenge from this tutorial
- Monthly: Review and refine your .vimrc
MEASUREMENT: Track your efficiency:
- Time how long common tasks take
- Count keystrokes for repeated operations
- Set personal bests and try to beat them
- Challenge colleagues to VI races
Final Wisdom
THE VI MASTER'S MINDSET:
A true VI master doesn't think in terms of individual keystrokes. They think in terms of transformations and objects. They see text as structured data to be manipulated, not as characters to be edited one at a time.
When a VI master looks at a file, they see:
- Paragraphs and sentences, not lines
- Functions and blocks, not characters
- Patterns and structures, not words
- Transformations and operations, not edits
They compose complex edits from simple commands. They automate relentlessly. They never repeat themselves. And they never, ever reach for the mouse.
You are now equipped to become that master.
PARTING ADVICE:
- Speed comes from thinking, not from typing faster
- The best VI users are lazy - they automate everything
- Your .vimrc is never finished - it evolves with you
- When you find yourself repeating something, make it a macro
- Read other people's .vimrc files for inspiration
- Contribute to the community - share your discoveries
YOUR MISSION:
For the next 30 days, challenge yourself to:
- Use VI for ALL text editing
- Learn one new command every day
- Create one new macro every week
- Never use the mouse in VI
- Time your common tasks and improve them
After 30 days, you'll wonder how you ever lived without VI mastery.
Created for Linux System Administrators | Part 5 of the VI Learning Series
You are now a VI Power User. Go forth and edit at the speed of thought!
← Back to VI Index
↑ Back to EXPANDED