Using Bash Instead of VimScript for Macros

! is really the magic wand of Vi. (Mr. Rob)

A lot of people know about how amazing Vim and Vi are but few of them realize that the ultimate pairing is with the Bash shell. When you understand this you never need to learn VimScript and will produce a more sustainable, portable workflow, and tool set. The secret is understanding the magic wand commands:

The Only Macros You Need
Keys Description
!! Send the current line to the shell and replace with output.
!} Send the current section to the shell and replace with output.
!G Send the rest of the file to the shell and replace.

These are wicked fast to type and pair well with any preloaded Bash function or executable reducing the need to even type the : key to zero.

For example, say I am maintaining a log of entries in Markdown and I want to make a new header for each. I might write some functions like the following and put it in my .bashrc:

now () { 
    echo $1 $(date "+%A, %B %e, %Y - %l:%M:%S%p")
}

hnow () { 
    echo $(printf '#%.0s' {1..2}) $(now)
}

Now to add a new header I just type !!hnow and get something like the following:

## Saturday, May 11, 2019 - 8:43:04PM

But it gets even better. You don’t even have to have a function or command. You can write them in your document and then pass them to bash so they replace themselves with the output:

Here is just a bunch of random stuff in a file:

for i in {1..10}; do echo $i. ; done

Now when I have the cursor on the for line I only have to do !!bash to replace it like this:

Here is just a bunch of random stuff in a file:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

This technique can be used to sort the content of a file and include it in what you are editing whether it be a document or source code. In fact, you can use this technique to write boring code quickly. This example is contrived, but you get the idea:

package main

import "fmt"

func main() {
for i in Rob Doris Ben Otto; do echo "  fmt.Println(\"Hello, $i!\")"; done
}

And after !!bash waving of the magic wand on the for line again we get:

package main

import "fmt"

func main() {
  fmt.Println("Hello, Rob!")
  fmt.Println("Hello, Doris!")
  fmt.Println("Hello, Ben!")
  fmt.Println("Hello, Otto!")
}

The possibilities are literally endless.

Compare this approach to the work to create a VSCode extension and you see why it isn’t just the Vim bindings that are valuable but the entire use of vim/vi on the shell that makes it the most powerful editor on the planet.

💢 When I hear about people leaving vi for Emacs because it has a better plugin system I just have to shake my head — or worse, when that person brags about how many lines of VimScript they had written before the switch. It is just so pathetically clueless.