Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

Persistent Growl notifications for long tasks UNIX
I have a tendency to forget that I'm in the middle of a backup -- I use an rsync script that runs in the background -- so I needed a way to keep a reminder on screen for the duration of the task so I don't accidentally disconnect the external backup drive.

This turned out to be easy using Growl and the growlnotify utility. By creating the note as sticky (using the -s option) and giving it an identifier (using the -d option), the notification will stay on screen and can be updated periodically as desired.

The Installer package for growlnotify is in the growlnotify folder in the Extras folder on the Growl disk image. A demonstration script (this assumes that growlnotify is installed in /usr/local/bin; more interesting code would replace the sleep commands, obviously):
#! /bin/bash

# open a new sticky growl notification with the designator 'note'
/usr/local/bin/growlnotify -d note -s 'Begin the Beguine'
sleep 10

# update notification 'note'
/usr/local/bin/growlnotify -d note -s 'Still doing that "Beguine" thing...'
sleep 10
# update notification 'note' again
/usr/local/bin/growlnotify -d note -s -m "beguiners can't be choosers." 'Beguine, Beguine, Beguine... '
sleep 10

# update notification 'note', again, and remove stickiness so it disappears
/usr/local/bin/growlnotify -d note "Eh. I'm over it.'"
The same thing can be done with AppleScript if you create long-running stay open scripts; just make sure you set the sticky and identifier properties.

[crarko adds: I tested this, and it works as described.]
  • Currently 2.88 / 5
  You rated: 3 / 5 (8 votes cast)

Persistent Growl notifications for long tasks | 5 comments | Create New Account
Click here to return to the 'Persistent Growl notifications for long tasks' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
Persistent Growl notifications for long tasks
Authored by: Anonymous on Oct 14, '10 08:37:52AM

Neat, but kinda hamfisted. With your approach, you need to write it into your script. So if you launched something without the notifications you're stuck. You need a way to monitor, rather than report.

Monitoring approaches would use a terminal window (or geektool?) doing something like tailing a log file ("tail --pid $PID -f $FILE") -- you are taking logs, aren't you? -- or a "watch pgrep -lf rsync". More effort would be needed to make use of growl.

The fun fact about monitoring, as distinct from reporting, is that it's applicable to other OSs (yup, even the one from Redmond), and gives you an inkling of what server folk do all day.

[ Reply to This | # ]
Persistent Growl notifications for long tasks
Authored by: tedw on Oct 14, '10 12:28:33PM
true enough. I was mostly interested in the persistent message thing. If you want to focus more on monitoring there's hint from a few years ago here that talks about how to set up something more generalized. combining these two might be a fun project; I just didn't bother. :-)

[ Reply to This | # ]
Persistent Growl notifications for long tasks
Authored by: Seth Milliken on Oct 14, '10 04:21:49PM
Here's a python script I use to do this more generally. With this script saved as executable in ~/bin/grun and ~/bin in my $PATH, I can precede any command with "grun" to have it report completion via growlnotify.

# grun
# Run a commandline that posts a growl notification when it completes.
# Each command gets its own individual sticky notification that is updated on completion.
# TODO: parameterize growlnotify location
# TODO: support && compound commands, e.g. (echo "Initial output." && sleep 5 && echo "Final output.")
from sys import argv
import os, subprocess, string

original_command = " ".join(argv[1:])

def postnotification(identifier, title, message):
    os.system("growlnotify --identifier " + str(identifier) + " --sticky -a '' -t '" + title + "' -m \"" + message + "\"")

p = subprocess.Popen(argv[1:])
postnotification(, "running shell command", original_command)
status = os.waitpid(, 0)[1]
postnotification(, "shell command completed", original_command + " (" + str(status) + ")")

[ Reply to This | # ]
Persistent Growl notifications for long tasks
Authored by: dfbills on Oct 15, '10 04:58:57AM


Why don't you share your actual script? I'd love to incorporate it into some of my rsync routines.


[ Reply to This | # ]
Persistent Growl notifications for long tasks
Authored by: tedw on Oct 15, '10 08:31:43AM
Sure thing. Sorry if the script isn't as elegant as it could be ( I'm a b-class unix programmer - :-] ). I launch this from a Folder Action that's attached to /Volumes. When I mount the backup disk the folder action starts this script, and when the disk unmounts the folder action checks to see if this process is still running (and if necessary kills it). I've added a few extra comment lines for your benefit; let me know if something's not clear (or if there's something I could have coded better)

One important point: I'm using an updated version of rsync (3.0.7), hence the /usr/local/bin/rsync path. Leopard and Snow Leopard ship with an older version of rsync (2.6.9), and the rsync people changed the options in a non-backward compatible way. the -X option is the 3.0.x option for including extended attributes. That doesn't exist in 2.6.9 (which uses -E for extended attributed; -E in 3.0.x is for preserving executability), so these rsync commands would throw an 'unknown option' error on a standard install. I updated because 3.0.x handles resource forks better, fyi.

#! /bin/bash

# store the rsync pid so that I can abort the process if the drive gets disconnected
launchctl setenv backupPID $$

# create a sticky notification
/usr/local/bin/growlnotify -d note -a Finder -s 'Backup Begun'

# set main backup path

# backup downloads folder, without revisions
/usr/local/bin/rsync -aXq --delete --prune-empty-dirs $HOME/Downloads $backupPath 

# backup movies folder, without revisions
/usr/local/bin/rsync -aXq --prune-empty-dirs $HOME/Movies $backupPath \

# modify sticky notification to show first stage done
/usr/local/bin/growlnotify -d note -a Finder -s 'Downloads & Movies Complete'

# revisioned backups stored in a folder called 'dated backups
datedDirectory="$backupPath"'Dated backups/'

# set comparison list for link-dest for home files. this collects a list of all
# the current dated backup folders and hard-links against all of them (slight increase
# in rsync time, slightly more efficient storage)
list=$(ls -1 "$datedDirectory")
for item in $list; do
	if [ -d "$path" ]; then

# create a new dated backup folder
date=`date "+%Y-%m-%d_%H.%M.%S"`

# back up selected home directories with revisions
/usr/local/bin/rsync -aXq "${linkDirs[@]}" $HOME/ "$datedPath" \
	--filter='+ /Library' \
	--filter='+ /Library/LaunchAgents' \
	--filter='+ /Library/Scripts' \
	--filter='+ /Library/Mail' \
	--filter='+ /Library/StickiesDatabase' \
	--filter='+ /Library/Desktop Pictures' \
	--filter='+ /Library/Application Support' \
	--filter='- /Library/*' \
	--filter='+ /Xcode Projects' \
	--filter='+ /Documents' \
	--filter='+ /Desktop' \
	--filter='+ /Music' \
	--filter='+ /Sites' \
	--filter='+ /Pictures' \
	--filter='+ /Applications' \
	--filter='- /*' 
# redo link-dest comparison list.  I lump all my modified system files in
# a single folder, so I need to focus the comparison folders there.
for item in $list; do	
	if [ -d "$path" ]; then

# back up selected system files with revisions
/usr/local/bin/rsync -aXq "${linkDirs[@]}" /etc/ "$datedPath/system" \
	--filter='+ /apache2' \
	--filter='+ /fstab' \
	--filter='+ /hosts' \
	--filter='+ /php.ini*' \
	--filter='- /*' \

# delete the stored rsync pid
launchctl unsetenv backupPID

# modify notification to give 'all clear' notice, and remove stickiness 
/usr/local/bin/growlnotify -d note -a Finder 'Full Backup Complete'

[ Reply to This | # ]