Nathan Grigg

Anonymous functions in zsh

My favorite shell, zsh, allows you to define a function without assigning it a name. This turns out to be incredibly useful, and for none of the reasons that anonymous functions are usually used.

Let’s say I want to copy four or five files from one place to another, but in a more complicated way than the standard cp command allows.

$ cp 20140508/old.txt 20140508/new.txt
$ cp 20140610/old.txt 20140610/new.txt
$ cp 20140731/old.txt 20140731/new.txt
$ cp 20140802/old.txt 20140802/new.txt

Obviously, you can just run these commands by using your shell’s history and editing each time. But editing is hard, especially in the middle of a line and in more than one place per line.

One way to solve this is with a for loop:

$ for d in 20140508 20140610 20140731 20140802; do
>   cp $d/old.txt $d/new.txt
> done

But this is not very flexible. If one of the commands fails, you will probably end up with some copies completed and others not. After fixing the problem, you will have to remove the copies that succeeded from the for loop.

Another way to solve this problem is write a function and then use it. (Note that in bash you need to use three lines to define the function.)

$ mycp() { cp $1/old.txt $1/new.txt }
$ mycp 20140508
$ mycp 20140601
$ mycp 20140731
$ mycp 20140802

This solves the problem of a single command failing, since you can fix it up, rerun the failed command, and continue along. But it also turns a one-step process into two. If I later find I need to do two more copies, I have probably opened a new shell, so I have to first redefine mycp before reusing it.

Or you can use anonymous functions:

$ () { cp $1/old.txt $1/new.txt } 20140508
$ () { cp $1/old.txt $1/new.txt } 20140601
$ () { cp $1/old.txt $1/new.txt } 20140731
$ () { cp $1/old.txt $1/new.txt } 20140802

Each time you run the command, you just have to find the previous command in your shell history and edit the final word. If you need to use this same command tomorrow, you can search your shell history, and again you only need to edit the final word.