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:

  1. Extract all unique IP addresses
  2. Count requests per IP
  3. Sort by request count (highest first)
  4. Show top 10
  5. 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:

  1. Find all print statements
  2. Replace with logging.debug
  3. Add import at top if not present
  4. 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:
  1. Week 1-2: Use VI exclusively. Resist the urge to use other editors.
  2. Week 3-4: Challenge yourself to reduce keystrokes. Count them.
  3. Month 2: Build your macro library. Record everything you do twice.
  4. Month 3: Master one text object type per week (quotes, brackets, tags).
  5. Month 4: Learn one new ex command per day. Use it that day.
  6. Month 5: Optimize your .vimrc. Map your most common patterns.
  7. Month 6: Teach someone else. Teaching reveals gaps in understanding.
DAILY PRACTICE:
MEASUREMENT: Track your efficiency:

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:

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:
YOUR MISSION:

For the next 30 days, challenge yourself to:

  1. Use VI for ALL text editing
  2. Learn one new command every day
  3. Create one new macro every week
  4. Never use the mouse in VI
  5. 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