Over and Over: quick ways to reuse handy shell one-liners

There are a few one-liners that I use in the shell to do some really nifty stuff. I struggle to quickly find and reuse these and asked for a solution on Mastodon.

https://fosstodon.org/@quantixed/109518367449656573

A handy one-liner might be something like (taken from here):

find . -iname '*.flac' -exec bash -c 'D=$(dirname "{}"); B=$(basename "{}"); mkdir "$D/mp3/"; ffmpeg -i "{}" -ab 320k -map_metadata 0 -id3v2_version 3 "$D/mp3/${B%.*}.mp3"' \;\n

This converts all the flac files in subfolders into mp3 files. Obviously, we don’t want to rekey this. And maybe it’s called once a week, with a lot of history in-between. How do we quickly find and reuse this handy one-liner?

To recap: to find a previously used command it’s possible to:

  • use the up arrow to find the command. This is great if it was the last one or two, not so great if it was 15 lines ago
  • call history 1 find the line that is needed and then do !500 for line 500. Works but is a bit clunky
  • it’s more efficient to do history 1 | grep yout or something to find only the lines that feature youtube-dl commands. But still not ideal
  • It’s possible to do ctrl + r and type in yout or something to find a previously line but I find this hard to use.
  • Store useful commands somewhere. I have Notable set up, in which I record all kinds of stuff including one-liners. I have handy code written in all kinds of places (gists, ELN, this website etc.). It gets hard to remember what is where, so I wanted a better solution.

I should say I am using terminal on macOS with zsh as the shell.

The responses

I got some very useful replies to explore:

  • auto-complete plug-in for zsh (suggested by Andrew)
  • espanso (suggested by Tom Stafford)
  • alias and functions (Hal Pomeranz) and a slightly more complex implementation of these (by Will Palmer)

I liked the look of espanso as a system-wide way of expanding text. It seems very useful, beyond the terminal, so I set it aside for now.

I first tried this zsh auto-complete plug-in. It is actively maintained on GitHub and was simple to install. As described it shows recently-used commands and is great for suggesting commands and directories etc. Unfortunately, I found that it would cause terminal to lock up. This was annoying because I lost my immediate history in the lock-up. It sped up the process of locating one-liners for sure, but it was still a little cumbersome. I uninstalled it and decided to look at aliases and shortcuts.

Aliases and functions

OK, so these were not exactly new to me, but I hadn’t been bothered to set them up. So here’s how I did it, if anyone wants to try.

An alias might be something like ytd instead of typing youtube-dl.

A function is something more like a chunk of code, where you would want to set some parameters via arguments.

Thinking about the one-liner example above, should it be an alias or a function? It’s arguable, but let’s call it an alias.

Now, both aliases and functions can go directly into the .zshrc file. But for tidiness it’s best to put them into a distinct file and get .zshrc to initialise that file when the shell starts.

Edit .zshrc

In home, open .zshrc with your favourite editor e.g.

nano .zshrc

Then add the following to the file:

# Alias definitions

if [ -f ~/.zsh_aliases ]; then
    . ~/.zsh_aliases
fi

This basically says, if there is a file called .zsh_aliases in home, then load it. Save .zshrc by ctrl + x and y.

So now, let’s make that file and edit it.

Make .zsh_aliases file

touch .zsh_aliases
nano .zsh_aliases

In here, you can list as many aliases as you like. They take the form

alias name="oneliner"

So for example, you might add:

alias hg="history 1 | grep yout"

And this would allow you to type hg on the command line to search for lines in the history that feature the string yout. This is not very useful, so let’s look at our previous one-liner.

find . -iname '*.flac' -exec bash -c 'D=$(dirname "{}"); B=$(basename "{}"); mkdir "$D/mp3/"; ffmpeg -i "{}" -ab 320k -map_metadata 0 -id3v2_version 3 "$D/mp3/${B%.*}.mp3"' \;\n

To add this, we’d need to add:

alias flac2mp3="find . -iname '*.flac' -exec bash -c 'D=\$(dirname \"{}\"); B=\$(basename \"{}\"); mkdir \"\$D/mp3/\"; ffmpeg -i \"{}\" -ab 320k -map_metadata 0 -id3v2_version 3 \"\$D/mp3/\${B%.*}.mp3\"' \;\n"

The thing to note here is that the original one-liner has a mix of single and double quotes, and we need to enclose all of that in quotes (either single of double). So we can use single quotes within a double-quoted one-liner, but if we have double-quotes they need escaping with a backslash. In addition, the $ character also needs escaping.

Once you have added all the aliases you need, simply save and exit. Remember that they won’t get loaded until you rerun .zshrc once more, which can be invoked by simply closing and reopening the terminal.

Now we can run this one-liner by simply typing:

flac2mp3

To add functions, you can simply add another if statement to your .zshrc file and make another file called .zsh_functions and put them there.

Thanks to everyone who replied on Mastodon, for their suggestions.

The post title comes from “Over and Over” by The Beat from their 1981 LP “Wha’ppen?”. Did you think I was going to say: by Hot Chip?