Desktop notifications after long running terminal commands finish

Reading time: about 3 minutes

When working with terminal utilities like Terraform and Ansible, sometimes the commands take several minutes to finish. For example, creating an RDS instance in us-east-1 takes about 15 minutes. I can’t stand simply sitting and staring at the intermittent messages terraform produces so I go do something else while it runs. I remember to check back at first, but after a couple minutes it’s out of mind and I end up not finding the finished task for an extra 20 minutes.

This is how I wired up macOS to send a system notification when a long running command finishes:


Fish config

# Functions which reside in and tap into fish shell system hooks
# To enable automatic alert, run `set -U _long_command_finished_notification true`

function exec_start --on-event fish_preexec -d "Starts the execution clock of a process"
  set -g _exec_start (date +%s)

function exec_end --on-event fish_postexec -d "Stop the execution clock of a process and set _exec_delta"
  set -g _exec_delta (math (date +%s) - $_exec_start)
  set -e -g _exec_start
  set -g _formatted_time (decode_time $_exec_delta)

function auto_alert --on-event fish_postexec -d "Check the execution delta and send an alert on long running commands"
  if test "$_long_command_finished_notification" != true

  if test $_exec_delta -gt 12
    set -l first_word (string split -m 1 " " "$argv[1]")[1]
    alert -m "$first_word command finished ($_formatted_time)"

These functions have been slightly edited. The originals are available in my dotfiles.

Fish functions

# Fish functions which reside in .config/fish/functions and are used as part of
# the functions triggered by fish events.

# ~/.config/fish/functions/
function alert -d "Send a request to hammerspoon for a system notification"
  argparse --name alert 'm/message=' 't/timeout=' -- $argv or return
  open -g "hammerspoon://task_completed?message=$_flag_message&timeout=$_flag_timeout"

# ~/.config/fish/functions/
function decode_time -d "Converts a unix timestamp delta into d:hh:mm:ss"
  # ported from / inspired by
  set -l seconds $argv[1]
  set -l days (math $seconds / 86400)
  set -l hours (math "$seconds / 3600 % 24")
  set -l minutes (math "$seconds / 60 % 60")
  set -l seconds (math "$seconds % 60")

  set -l sent_days 0
  set -l sent_hours 0
  set -l sent_minutes 0
  set -l printable ''

  if test $days -gt 0
    set printable $printable{$days}d
    set sent_days 1

  if test $hours -gt 0 -o $sent_days -gt 0
    test $sent_days -gt 0; and set printable $printable{' '}
    set printable $printable{$hours}h
    set sent_hours 1

  if test $minutes -gt 0 -o $sent_hours -gt 0
    test $sent_hours -gt 0; and set printable $printable{' '}
    set printable $printable{$minutes}m
    set sent_minutes 1

  test $sent_minutes -gt 0; and set printable $printable{' '}
  echo $printable{$seconds}s

In my dotfiles: decode_time and alert

Hammerspoon config

-- ~/.hammerspoon/init.lua
hs.urlevent.bind("task_completed", function(eventName, params)
  local message = params['message']
  local timeout = tonumber(params['timeout'])

  if not message or message:len() == 0 then
    message = "Long running command completed"

  if not timeout then
    timeout = 11

  local notification = end,
      autoWithdraw = true,
      title = "Terminal Notification",
      informativeText = message,
      hasActionButton = false

  if timeout > 0 then
    hs.timer.doAfter(timeout, function()

This function in my dotfiles.

More information

The entire patch adding this feature to my dotfiles

Fish shell documentation:

Hammerspoon documentation:

Date: 2018-May-04
Tags: macos terminal hammerspoon automation notification productivity
Previous: Hammerspoon Clipboard Autosuggest