Menu ☰

Automating Tasks

Warmup activity: the bash shell

The Terminal.app application is actually a "terminal emulator," which allows you to use a "command-line interpreter" that uses the "bash" shell program to interact with your computer. There are other terminal emulators and there are other shell programs that you can use—I often use the iTerm application as my emulator, for example, although I always use the "bash" shell, which takes the commands I enter— ls, cd, etc.—and executes them.

Just as you can write Java or Python programs, you can also write shell programs—usually called shell scripts—that do all sorts of things. In some cases, those scripts can be written using a single line of code, with statements separated by semicolons (;).

Here's a fun, single-line shell script you can run (Apple OS X only)

Activity: ScreenCapture (OS X only)

  1. Launch the Terminal
  2. cd to the Desktop
  3. mkdir a directory (folder) called "screenmonitor"
  4. ls to confirm that you've created that directory
  5. In the terminal, issue the command listed here. Note that the character ` is the "backtick" character at the upper-left corner of your keyboard (just left of the "1" key).
    screencapture -x ~/Desktop/screenmonitor/`date +%Y%m%d%H%M%S`.jpg
    If no output is produced, the command was executed. If you get an error message, however, see if you can figure out what you did wrong.
  6. Using your mouse, navigate to the "screenmonitor" folder and double-click the file it contains. It's a capture of your screen at the time you issued the command!
  7. Now that we've confirmed the screen capture works, enter this single-line shell script into the Terminal and press [Enter] to run it.
    while x=0; do screencapture -x ~/Desktop/screenmonitor/`date +%Y%m%d%H%M%S`.jpg; sleep 30; done;
    As long as this script is running, it will silently take a screenshot of your monitor every 30 seconds and store it in that directory.
  8. What uses might there be for this kind of utility?

For reference, here is a more extensive bash program that does the same thing you just did in a series of commands.

#!/bin/bash # This bash script silently captures the screen every 30 seconds that the computer is awake, and stores the captures in a folder. if [ ! -d "/Users/rwhite/Desktop/screenmonitor" ] then echo "screenmononitor directory doesn't exist on Desktop" mkdir "/Users/rwhite/Desktop/screenmonitor" echo "Added directory" else echo "Using existing screenmonitor directory on Desktop for screenshots." fi while x=0; do screencapture -x ~/Desktop/screenmonitor/`date +%Y%m%d%H%M%S`.jpg; sleep 30; done;

Intro to cron

The cron utility is a daemon, a small program that runs without user intervention. This cron program is launched every minute that your computer is in operation, at which time it checks to see if there are, in turn, any programs that it is supposed to run. As a user, you can write small programs that you wish to run automatically on a given date or time, and create an entry in the cron table, or crontab interface.

Let's see how easy it is to do that.

Launching a webpage from the browser

I like to start my day reading the Penny Arcade comic. I can open by browser and type in the URL, and I might even make a bookmark in my browser for that page: https://www.penny-arcade.com/comic

Conveniently, I can also launch that webpage on my Apple OS X machine using the open command in the Terminal:

$ /usr/bin/open https://www.penny-arcade.com/comic

In Ubuntu (Debian Linux), one can launch the default terminal using the x-www-browser command:

$ x-www-browser https://www.penny-arcade.com/comic

Go ahead and try it!

Writing a script

Once we've confirmed that the command works, let's write a shell script that will run that line for us. This shell script, when executed, will issue the Terminal instruction that we just tried out above.

We're going to call the script penny_arcade_launcher.sh, and we're going to save it in an OS X directory called /usr/local/bin/, as shown below.

Create the following shell script using a text editor.

#!/bin/sh /usr/bin/open https://www.penny-arcade.com/comic

Here's what it looked like when I created that shell script using nano:

Confirm that your script works by opening up a Terminal and using the bash shell to run the script:

$ bash /usr/local/bin/penny_arcade_launcher.sh

You should see the page launch from that script!

Setting up a cron job

Now that we have a script, we can set up a cron job that will launch that script at a pre-specified time. If I want to read the comic when I start my day at 8am, I'm going to set up cron to run that script at 7:30 in the morning, Monday through Friday.

$ crontab -e

This launches your default text editor so that you can create an entry in your cron table. If you haven't yet set a text editor for your shell, you may be presented with something like this:

no crontab for leee - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/ed 2. /bin/nano <---- easiest 3. /usr/bin/vim.basic 4. /usr/bin/vim.tiny Choose 1-4 [2]:

Once you've selected an editor, you can add a line to the crontab similar to the one below, which uses bash to run the script at 7:30 every day of the week, Monday through Friday:

30 07 * * 1-5 bash /usr/local/bin/penny_arcade_launcher.sh

The instruction to start the launcher using bash is the last entry. The five fields in front of that instruction indicate when the command should be run.

From the Wikipedia entry on cron:

# ┌───────────── min (0 - 59) # │ ┌────────────── hour (0 - 23) # │ │ ┌─────────────── day of month (1 - 31) # │ │ │ ┌──────────────── month (1 - 12) # │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0) # │ │ │ │ │ # │ │ │ │ │ # * * * * * command to execute

The asterisk * indicates that the job should be run during every time period indicated. So, our launcher will run at 7:30 every day of the month, every month of the year, Monday through Friday.

In OS X, cron is now deprecated in favor of using launchd. Although cron is still supported, you may wish to consider creating a launchd plist instead.

See below for more information on setting that up.

Advanced Users: Setting up launchd

cron has been deprecated on many systems in favor of a more powerful and versatile (and more complex) system called launchd for "launch daemon." This launch daemon is responsible for managing the running of files on your system in the same way that cron is, but configuring it takes a bit more work.

We're assuming you already have your penny_arcade_launcher.sh shell script set up and stored in /usr/local/bin/.

  1. Create your plist file

    Here's an example that you can start from. You should call this file something like com.crashwhite.launchPennyArcade.plist.

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.crashwhite.pennyarcadelauncher</string> <!-- The job that should be run --> <key>ProgramArguments</key> <array> <string>bash</string> <string>/usr/local/bin/penny_arcade_launcher.sh</string> <string>sleep</string> <string>10</string> </array> <!-- A job that should run at a certain day/date/time --> <key>StartCalendarInterval</key> <array> <dict> <key>Hour</key> <integer>23</integer> <key>Minute</key> <integer>25</integer> <key>Weekday</key> <integer>1</integer> </dict> <dict> <key>Hour</key> <integer>23</integer> <key>Minute</key> <integer>25</integer> <key>Weekday</key> <integer>4</integer> </dict> </array> <!-- A regularly repeating job --> <key>StartInterval</key> <integer>120</integer> <!-- Run it when it is first loaded? --> <key>RunAtLoad</key> <true/> <key>UserName</key> <string>rwhite</string> </dict> </plist>
  2. Move this file to the LaunchAgents directory

    For this launchd file to work, it needs to be placed in the appropriate directory. In OS X, user files are placed in ~/Library/LaunchAgents. (There are other places the file may run as well, but we're going to stick with this for now.)

  3. Load your file using launchctl

    You now need to tell launchd that you'd like this agent to be included with all the other things that launchd runs from time to time.

    In the Terminal:

    $ launchctl load ~/Library/LaunchAgents/com.crashwhite.launchPennyArcade.plist

    If you don't get any error messages, then your agent is being monitored and should be run at the time and day you've indicated.

Advanced Users: Taking a picture (OS X only)

Just as the script above took a screenshot every 30 seconds, you can write a script that takes a picture using the webcam every 30 seconds as well.

To do this you'll need to install ffmpeg onto your computer.

Then, write and run the following script:

#!/bin/bash # This bash script takes a picture with the webcam every 30 seconds that the computer is awake, and stores the captures in a folder. if [ ! -d "/Users/rwhite/Desktop/screenmonitor" ] then echo "screenmononitor directory doesn't exist on Desktop" mkdir "/Users/rwhite/Desktop/screenmonitor" echo "Added directory" else echo "Using existing screenmonitor directory on Desktop for screenshots." fi while x=0; do ffmpeg -f avfoundation -video_size 1280x720 -framerate 30 -i "0" -vframes 1 /Users/rwhite/Desktop/screenmonitor/`date +%Y%m%d%H%M%S`.jpg > /dev/null 2>&1; sleep 30; done;