Using wrapper scripts to keep track of long-running commands
You know that script?
You know, the one that takes so long to run that you get bored, start reading Hacker News, forget about it until an hour later, and then you’ve lost 45 minutes of productive time?
Or maybe it’s something else: a Continuous Integration build, or that job you have to run every now and then to reindex your development database, or reticulate your splines, or whatever.
It sure would be nice if we could make it faster, but sometimes that’s not possible, or not practical. Still, failing that, it’d be good if we could get that 45 minutes of “forgotten time” back, wouldn’t it?
I found this sequence of thoughts coming up for me pretty regularly, so I decided I’d try to do something about it.
I wanted the notification to tell me a few things:
- That my thing was finished.
- Preferably, what the result was.
- If it could remind me what I was doing, then that’d be nice too. (This would also allow me to have multiple long-running tasks on the go, and make sense of the notifications for each of them.)
I spend a lot of time in the command line, and most of my long-running tasks happen either there or in the cloud, so I decided to start with something that would tell me when my terminal command was finished.
I found that it’s quite easy to make a thing that just says “Hi” after another thing completes. You just make a thing that says “Hi”, and invoke it like this:
$ command-i-have-to-wait-for; say-hi
Or, for a slightly more concrete example on OSX:
$ sleep 300; osascript -e "display notification \"Hi\""
This is fine for the first part of my requirements, but not so great for the other parts.
To get the result of my command, I could use some more Bash magic:
$ do-something && say-hooray || say-boo
That’ll work, but I’m pretty impatient and I don’t like typing, so maybe I can do something that will be easier to use. (It also doesn’t have any way to tell me what it was that finished.)
So I let this idea sit and percolate for a few months, and eventually I came to the conclusion that the least worst thing I could do would be a wrapper script. It would know the command, could wait for it to finish, and then could do arbitrary things like telling me how it turned out.
Wrapper scripts have some pitfalls, though, and I’d need to work around them:
- A wrapper could override the exit code of my original command, and I might need that exit code!
- A wrapper might “steal” my STDIN, which sucks if I have to wait for something that takes input from a pipe.
- If it has output (e.g. warning messages) a wrapper might pollute the output of the wrapped command with irrelevant stuff.
So I’d need to make a wrapper that:
- Exits with the same exit code as the wrapped command.
- Passes any input on STDIN through to the wrapped command.
- Remains silent if at all possible, and if it must produce output, puts it on STDERR.
It took a little work (figuring out the right arguments for
bash had me stumped for a while), but eventually I came up with notify-done.sh. I use a Bash alias to abbreviate it to
n, so I run it like this:
$ n sleep 5
And it notifies me like this:
So that works pretty well.
But what if, instead of reading Hacker News, I go to the kitchen to grab a snack?
Enter the Slack notifier!
I’ve discovered I have a good reason to make a second wrapper script: one that notifies me via Slack instead of the OSX Notification Centre.
We have all the same caveats, pitfalls and bugbears, plus one extra: Slack uses a web API. So we’ll need to figure out stuff like authentication, and implement a (very simple) API client in Bash, because I can’t be bothered with HTTParty today.
Fortunately, Slack’s Incoming Webhook integration looks right for this purpose, and is documented well enough that I think I can use it. It also has the advantage (for me) that the webhook URL contains the authentication secret, meaning that once I’ve set up the webhook, I don’t have to worry about a login process for the script.
The downside here is that I need permission to create Slack Apps in whatever Slack workspace I want to be notified on. For me this is easy — I created a free workspace a while ago, for experimenting with stuff like this. For you, depending on your circumstances, it may not be so easy.
I won’t go into heaps of detail on the code here and now; instead, here’s a link to the GitHub Gist for
slackme. I've tried to write it in a somewhat literate style, though I'm still learning about how to do that well, and I'm a long way from doing it the way Knuth would (though I'm not sure that's warranted in this case anyway)!
In any case, I hope it’s useful and enlightening, and saves you some “lost time” 😛