Multitasking and Keeping Track of Tasks
This is about the art of starting many tasks and keeping track of their progress and completion with on-the-fly notifications, with no special software.
Preemptive multitasking operating systems are a blessing, particularly for people like me who grew up with cooperative multitasking. It's tempting to start many tasks in the background and move on. But it's easy to lose track of them, especially if, like me, you typically have over 300 windows opened.
I'm not going to discuss here how having a good task manager allows you to keep track of things, make you multitasking and let you carry out many jobs at the same time, as would a scheduler. I'm more interested here about how to make sure you're notified when a task you started hours or days ago finishes. And it's not by mail.
1 Notifying Execution End
The idea is to ring the bell after a long-running command exits. All it takes it to write:
% long-running-command; echo -e \\a
If you already have long-running-command
running and forgot to type echo -e \\a
, you can still do so, even if there's a lot of output printed out. Make sure it's a command that doesn't expect anything from the TTY, e.g. prompts for information. Things will look like this:
% long-running-command
blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah echo -e \\a blah blah
blah blah blah blah blah blah
Soon enough, you'll want to define an alias along the lines of:
alias beep='echo -e \\a'
All you have to type then is things like long-running-command; beep
. Clearly, you could have your shell run this as a post command every time a command ends. You need to ask yourself then if you really want to be notified for each command ending – long-running as well as short-running ones.
In fact, shells such as zsh have the REPORTTIME
parameter which can be set to a number of seconds after which a command is considered long-running and the time spent will automatically be printed out after its completion. Maybe a bell could be rung only then too.
2 Urgency Hints
Ringing a bell for long-running commands is not ideal or useful per se, and it will make your computer annoyingly beepy. To make this bell desirable, we need to:
- make it visual;
- make it contextual, so that you know where the alert came from, i.e. which long-running command, which window, etc.
By making the bell visual, I don't mean to turn on flashing of windows to replace beeping. I mean to set a so-called urgency hint for the window manager to do something with it. Urgency hinting is a useful X11 feature which allows windows to express urgency to the window manager. The window manager can in turn translate this urgency into e.g. turning a window red to draw attention, or raise a notification more centrally.
Not all X11 programs, i.e. not all terminal emulators can emit urgency hints and not all window managers will receive them. But I've been using them flawlessly with rxvt-unicode and I know that window managers such as i3, xmonad and Notion understand them very well.
So you need to have your terminal set the urgency hint upon bell and with rxvt-unicode
this is done by adding this line to your ~/.Xresources
, before running xrdb ~/.Xresources
and restarting any rxvt-unicode
process:
URxvt.urgentOnBell: True
Notion, as well as some other window managers, will already do something useful with the window that set the urgency hint. Notion will turn it red. If it's not in view, it will light up a notification in the corner of the screen with the name of the window. And the window list will keep the urgent windows at its top so you can immediately jump to these.
3 Watching Output for Change
In some cases, you don't want to be notified when a command exits but when it prints out something or, more generally-speaking, when something changes in the terminal. This can apply to e.g. tail -f
s, interactive ncurses programs or watch
. In fact, watch
can be used in a variety of cases, for instance when waiting for a particular program to print some specific output. Here I'm using watch
to periodically run awk
which will print the number of bytes written by process 24902 only if it's more than 100 MB:
% watch -t 'awk "/write_bytes/ && \$2 > 100000000" /proc/24902/io'
Another simpler example I'm going to discuss here is using watch
to periodically run ls
piped into grep
when I expect file foo
to appear soon:
% watch -t 'ls | grep foo'
I could have done this with a loop, but I need to take care of sleeping myself:
% while true; do ls | grep foo; sleep 1; done
Of course, there is nothing there that will draw my attention, nothing emitting the urgency hint, nothing ringing the bell. I could of course write a slightly more sophisticated command line and echo \a
only if grep
exits 0.
% watch -t 'ls | grep foo && echo -e \\a'
What I've chosen to do instead is to write a Perl extension to rxvt-unicode
to ring the bell whenever a line is added (or otherwise changed):
sub add_lines { shift->scr_bell; () }
Of course, I don't want this all the time. In fact, I've got a keyboard shortcut to enable or disable this depending on whether or not I want to be notified of changes in a given window. I've even got a small, overlaid message telling me whether this is enabled on a per-window basis. More on this in my rxvt-unicode
Perl Extensions story.
The advantage of this approach is that it also works with ncurses-like programs, e.g. mutt
, and not just non-interactive programs where I can add echo -e \\a
s where I want.
4 Sleeping Before Starting
Not directly related to being notified, so much than it is about not forgetting to run commands later, I like to run sleep
before running some command which I want to be run only later. I could use at
, of course, but I want to keep the command line specific and attached to a terminal.
A typical example is when I've git push
ed some Puppet manifest changes and want to try them out on a given node. Knowing that the Puppet master may not have received the changes yet, I programmatically sleep for some time before running Puppet. And then I want to be notified when the Puppet run finishes:
% sleep 30s; puppet agent -tv; echo -e \\a
During all the time this command line is executed, I can do something else but still be notified when it's all over.
5 References
- i3
- xmonad
- Notion
- Manual page for watch
- Manual page for sleep
- Manual page for rxvt-unicode
rxvt-unicode
Perl Extensions- Manual page for zshparam