This is a way to get the output of remind, a Unix program, to show up as a Growl notification using cron. This is necessary because growlnotify, which is Growl's command-line client, won't work from a cron script. To do this, you'll create two scripts, a "server" script and a "client" script. Update: I have successfully used this tip on Tiger (10.4) as well as Panther (10.3).
Obviously, you'll need to have Growl installed. It should also be running and waiting for notifications. You'll also need to have remind installed. Finally, you'll need to install the Growl bindings for Perl, which are a separate download from the main Growl application. In order to install remind and the Growl Perl bindings, you'll need to have the Xcode developer tools installed.
First, create a server Perl daemon and call it "growltest.pl". I'm not exactly an expert on Growl, so this script is a bit crude and doesn't have a lot of features. Obviously, if you're at all handy with Perl, you could absolutely fix this up and make it much nicer. (Don't type in the line numbers, they're just for the notes).
1 #!/usr/bin/perl -w 2 3 use strict; 4 use Mac::Growl qw(:all); 5 use IO::Socket::INET; 6 7 #just a global: 8 my $listenPort = 9010; 9 10 # set up some notifications: 11 my @allNotifications = qw(msg); 12 my @defaultNotifications = qw(msg); 13 14 # Register them with Growl: 15 # "GrowlTest" is the name of the application for Growl's purposes. 16 RegisterNotifications("GrowlTest", \@allNotifications, \@defaultNotifications); 17 18 # set up a socket for listening: 19 my $server = IO::Socket::INET->new(LocalPort => $listenPort, 20 Type => SOCK_STREAM, 21 Reuse => 1, 22 Listen => 10) 23 or die "Couldn't be a tcp server on port $listenPort: $!\n"; 24 25 #now listen for connections: 26 while (my $client = $server->accept()) 27 { 28 #print qq(Got a connection\n); 29 my $request = <$client>; 30 #print qq($request\n); 31 PostNotification("GrowlTest", "msg", "Remind Notification", $request, 0); 32 }
In line 11 and 12, set up some notifications for Growl, then register them in Line 16. Lines 19 to 23 the socket. This socket will listen on any IP address related to this machine. You can also bind the socket to a specific network interface on the machine.
Line 31 is the actual notification. This one isn't set up to be a sticky, but if you wanted it to be a sticky, just change the last "0" to a "1". The Parameters for posting notifications can be found in the documents for Mac::Growl. Type:
perldoc Mac::Growl
to learn more about Mac::Growl Perl bindings.
This is the easiest way to start your daemon if you're using Panther (10.3): Open a Terminal window and start the server daemon:
nohup perl /path/to/growltest.pl &
Using nohup lets the server daemon keep running even when you close the Terminal application. The & character at the end sends the process to the background and returns you to a shell prompt. When your daemon starts, there'll be a couple of lines in the Terminal window:
[1] 2753 sending output to nohup.out
The first line shows your Process ID. You can use this to kill your daemon from the Terminal window. You can use all the usual Unix tools in a terminal window to find and kill your daemon, as well as the Activity Monitor in your Utilities folder. The second line informs you that there's a log file in the same directory as your script. You can print logging messages there if you wish. I have commented out two diagnostic print statements in the script above, but if you don't they'll be printed into the log.
You can start your server daemon using the steps outlined previously in the Panther section. Or, use Apple's new launchd program. Create a new property list with the Property List Editor app that's part of your Xcode installation. Use the following keys (customize the values for your own installation):
Now save this property list file. I saved mine in ~/Library/LaunchAgents
. That's in my home folder. If LaunchAgents
folder doesn't already exist, create it. Pay attention to the capitalization. Test loading the daemon in the Terminal app:
launchctl load ~/Library/LaunchAgents/com.cminow.growltest.plist
If there are no errors, you should get returned to the shell prompt in a few moments. If there are errors, fix them and try again. Once you've loaded the daemon, you can unload it like so:
launchctl unload ~/Library/LaunchAgents/com.cminow.growltest.plist
Once you've got this set up, your daemon will start up whenever you restart your computer.
I had to make one change to the rem script. When it's run from cron, you need to supply it with an explicit path. It's probably in "/usr/local/bin", find the line:
EXECUTABLE=remind
and change it to:
EXECUTABLE=/usr/local/bin/remind
The client script is very simple:
1 #!/usr/bin/perl -w 2 3 use strict; 4 use IO::Socket::INET; 5 6 my $reminders = eval {`/usr/local/bin/rem` }; 7 $reminders =~ s/\n\n|\n/\r/g; 8 9 my $socket = IO::Socket::INET->new(PeerAddr => 'localhost', 10 PeerPort => 9010, 11 Proto => 'tcp', 12 Type => SOCK_STREAM) 13 or die "Couldn't connect with server: $!\n"; 14 15 print $socket "$reminders\n"; 16 17 close($socket);
Line 6 runs rem and stores the output in a variable. Line 7 converts the Unix line endings from the .reminders file to Mac line endings, which appears to be what Growl wants. If you don't change this, you don't get multiple lines of output in the Growl notification.
Line 9 sets up the socket. You could, if you wanted, set it up so that lines 9 through 17 were in a loop and send to multiple addresses by reading the peer addresses from a file.
Just call the perl client script from your crontab as often as you like, and you'll get reminders on your desktop...
Charlie Minow
February 9, 2020 - 5:08 PM MST