Today, I'll present a module for zsh that I wrote few days ago. The aim of this module is to provide a way to create easily temporary shell scripts, and save their favorites. If you are familiar with Emacs, and if you think about its macros, you're right! I designed this with the macro concept in mind.
I had the idea when working with people who aren't familiar with shell scripts, and who don't want to try it for helping them. The original example was a work-flow with a TODO to update regularly. And yet commands to do were not so difficult:
$ git add TODO $ git commit -m "Update the TODO." $ git stash $ git pull --rebase $ git stash pop $ git push
In reality, my coworker didn't plan to stash nor pull, but since this is my story, I can change it a little! :)
I thought that it is easy to write a script to make that works. I just have to copy these lines and paste them in a script. But, on one hand I find this boring, on the other hand someone who is not interested in scripts will never do that. So I had to find a transparent way for the user to have the same result without having the feeling that he plays with scripts. It's here that came the idea to mimic the behavior of the Emacs macros.
Notice that even if the Emacs macros works on text, and the title of this post might be confusing, I don't want to write a tool to enhance the Emacs macro in the Zsh command Line Editor (zle), but I want a way to create simple scripts in a easy and fast way.
1 Zsh macros!
I first tried to create a Perl script that can create scripts, but I
realized that there is too many drawbacks (no history, no
completion…). So I found an alternative way, fully integrated in
zsh: hooks. For people who doesn't know what are hooks: It is a set of
functions called at a specific time that allow the user to run their
own functions. It is useful for letting the user personalizing a
software. As an example, I wrote a git hook to check the log message
(I talked about it in a previous post). For this module, I use the
preexec hook for achieving my goal. In this post, I'll
present my module, why it can be useful for day-to-day usage, and how
to use it. In some next posts, I'll show some useful tricks I had to
use to make it works.
1.1 Why using it?
Because it allows you to save your time. It is easy to install, easy
to learn and easy to use. I realized that sometimes I repeat the same
sequence of lines several time. It ends up by having these lines
concatenated in one line with a
&& between them. Pretty ugly, right?
But because it is just repeated less than ten times, I don't want to
write a script for that because it is faster for me to just reuse my
zsh history. But with this module, you just type your sequence of command
once, and then you just have to hit
macro_execute to get it
repeated. Personally, I have aliased this command to
e. It is the
fastest and cleanest way I know to repeat your work properly.
1.2 How to install it
Glad to see you here! You'll see that it is a good choice :) The first
step to install it is to get it from my github (directory
zsh_macros). Once it is done, you just have to
source the file, in
your shell to try it, or in your configuration file if you're sure
that it will fit to your needs.
Some things to check: if you have already bound either
<ctrl-x><)>, I don't bind anything (I don't want to mess up your
own configuration!). These bindings are the same than under
Emacs. Feel free to adapt these bindings to your own wishes!
You then have to add something in your configuration file:
setopt prompt_subst RPROMPT='$(zmacros_info_wrapper)'
The first line allows the prompt to perform command substitution in
prompts. The second one set your RPROMPT to the value of
zmacros_info_wrapper that allows you to know the status of your macro.
If you have already assigned something to your RPROMPT, you could
simply add it to it.
Once this is done, everything should work fine. If this is not the case, you can either send me a bug report or send me a patch. Now, let's see how use this module.
1.3 How to use zsh macros?
In this part, I assume the bindings are the one originally
provided. I think a screenshot may help to figure out how it looks like, I
first run it in the shell, to show what you have to do, and what are
the result. Between
<> are represent the keyboard macros. Do not
write it out :).
$ echo foo foo $ <ctrl-x><(> echo bar bar $ echo baz baz $ <ctrl-x><)> e bar baz
This screenshot shows how it appears for the user:
The flag on the right (
<R$id>) appears right after you type
<ctrl-x><(>, and disappear right after you type <ctrl-x><)>.
Pretty easy right?
Note that if you don't like key-bindings (Are you a Vim user?), you
macro-end and you'll get the same
Let's go a little deeper: you can have several macro. This module
doesn't support nested macros in a same shell, but you can make as
many macros as you want. Each macro is associated with an id. This is
what is printed on the flag after the
R in the prompt. You can run
macro-execute with an optional argument that corresponds to the id
of the macro you want to run. By default it's the last
recorded. Notice that each script has its own file, and there is a
master file that track each of them. To add and execute macros, we
read and write on this file in /tmp. This way has its advantages and its
drawbacks. We have concurrency problems, but since a real user can't
make several things in the same time, that should not be a real
The advantages are that a macro recorded in a shell can be used in
another one, and you can register two macros at the same time, because
the only access to the main file is made when you call
macro-record. So recording two macros in two different shells is fine.
All your scripts live as long as your /tmp is not cleaned. If you want
to keep a macro for a longer use, it is possible. You just have to
call macro-name that will take the macro you asked for (if you give no
id, the last one is considered), and copy it in the directory
ZMACROS_DIRECTORY. You can set it at the top of the file
macros.zsh. Maybe it is a good idea to add this directory to your
path, it will allow you call these new functions simply.
This is the features available in this first version of the zsh macros. I planned to add some new ones, but if you have any request, comment, or anything, feel free to comment this post! I'd like to know what would be helpful and what you think about this module.