Batch Editing Files with Vim
Vim: great for trapping your coworkers, but also a ridiculously powerful batch-editing beast. Let’s process some files from the terminal without even looking at a UI.
Here’s our victim, post.md:
---
title: Awesome tips
tags:
- tech
---
An awesome post.
1. Quick & Dirty Command Line Edits
Need to change “tips” at the end of a line to “Tips”? Skip the interface and
just yell at Ex mode (-e) silently (-s):
vim -es -c '%s/tips$/Tips/' -c x post.md
-c: Runs an Ex command.x: Save (only if changed) and quit, quote it if you really want.wqis for people who love unnecessary disk writes.
2. When Things Get Complicated: Vim Scripts
Let’s say you want to:
- Fix that “tips$” typo.
- Inject a new
tiptag. - Sort the tags list.
Jam it all into an edit.vim file:
" 1. Fix typo
%s/tips$/Tips/
" 2. Append a tag like a civilized person (dot means "end of text")
/tags:/a
- tip
.
" 3. Sort and uniq (:help sort)
/tags:/+1,/^---/-1 sort u
" Save and exit
x
Run it with -S (source):
vim -es -S edit.vim post.md
Or violently redirect it into standard input:
vim -es post.md < edit.vim
3. Scriptless Chaos (The Inline Way)
Too lazy to make a script file? Chain your -c commands.
To insert the tag here, we ditch a (append). Why? Because a expects a
multi-line newline-terminated block, which is a nightmare to escape in a bash
string.
Enter the put trick. put =' - tip' evaluates a literal string expression
(the =) and drops it right below your cursor. Boom. One line.
vim -es -c '%s/tips$/Tips/' -c "/tags:/put =' - tip'" -c '/tags:/+1,/^---/-1 sort u' -c x post.md
(Warning: Quoting might cause mild brain damage.)
4. xargs Will Break Your Heart
You want to edit a hundred files. Your brain says, “Hey, xargs!”
find . -name '*.md' | xargs vim -es -S edit.vim
Vim hangs, and you curse after several attempts. Why? Because vim processed the first file, closed it, then opened the second and was waiting for more inputs!
“But wait!” you say, “I’ll use -n1!”
find . -name "*.md" | xargs -n1 vim -es -S edit.vim
Congrats, it works. But now you’re spawning a brand new Vim process for every single file. If you have a massive codebase, this is glacially slow.
The Real Solution: argdo
Pass all files to a single Vim instance and let Vim iterate through them like the champ it is.
vim -es -c 'argdo source edit.vim | update' -c qa *.md
argdo: Do this to everything in the argument list.update: Save only if changed (again, disk writes!).qa: Quit all.