Using wrapper scripts to keep track of long-running commands

Photo of a computer screen showing a Terminal window with columns of data in green text on a black background.
Photo by Joan Gamell on Unsplash

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.

Requirements

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.

Options?

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:

Or, for a slightly more concrete example on OSX:

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:

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.

Wrangling wrappers

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:

And it notifies me like this:

A Mac OSX notification entitled “Success”, that reads “0: sleep has completed.”
A Mac OSX notification entitled “Success”, that reads “0: sleep has completed.”

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” 😛

Happy notifying!

Redbubble

Creating the worlds largest marketplace for independent artists & bringing more creativity to all

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store