Dave, when I go to lunch, I have a suspicion that one of my coworkers jumps onto my iMac and pokes around. How can I catch them in the act?
Many organizations actually view computers in the workplace as shared property, not that of an individual so before going any further, you might want to ask HR (or your boss) if it’s okay to password lock your system. If everyone does it, you could just lock your computer while you’re gone, which might solve the problem. Neither really works and you just want a surreptitious utility that can spy on the screen and see what’s happening? Doable!
Like other modern operating systems, MacOS is actually built atop a version of the venerable UNIX operating system. This means that not only does it have an easy command line interface, but that you can also run things that aren’t visible on the screen, known as background processes. This is, in fact, a job for shell scripts, lightweight little programs that are run by the “shell” or command line interface itself on your Mac.
It’s built around a super helpful MacOS command line utility called screencapture. First, however, you have to set up a few things…
GRANT PERMISSION TO THE TERMINAL
In the Applications folder is another folder called Utilities. In there is an app called Terminal. Launch it and you’ll be staring at a prompt: Your interaction with the program is through the keyboard, not mouse or trackpad. To get permissions all set up, you’ll need to manually request a screen capture from the program. That’s done by carefully typing in this command:
$ screencapture test.png
You’ll hear the snapshot sound of a camera shutter and the file test.png will appear. It won’t have what’s on your screen, however, as shown here:
Look closely and you’ll see that the top menubar is appearing, but where are all my windows and file icons? The screenshot program (and, by extension, the shell and the Terminal app) doesn’t have permission to access the screen. MacOS being helpful!
You’ll see this pop up to clarify what’s happening:
This is expected since the goal is to capture everything on the screen. Click on “Open System Settings“.
You can see that I have granted this permission to KalturaCapture, Messenger, Microsoft Edge, and Zoom. Terminal needs it too, so a click on the slider adjacent solves the problem. Well, almost…
Either use the biometrics on your iMac, if there’s a fingerprint scanner, or enter your administrative password.
Still not quite done with setup, however…
Restart Terminal and try that screen capture command again. This time, you get a saved image that includes all the app windows, file icons, etc:
We’re now set up and ready to proceed!
USING SCREENCAPTURE ON THE COMMAND LINE
At this point you can set up a delayed screen capture by using multiple shell commands in a sequence. This is typically done by separating the commands with a semicolon, so:
$ sleep 300; screencapture delayedpic.jpg
This would delay 300 seconds (5 minutes) and then capture what’s on the screen, saving it to “delayedpic.jpg”.
Turns out that like many command line utilities, screencapture has a LOT of different options you can enable or disable. You can most easily learn about them by typing in screencapture -h:
It’s quite overwhelming, so I can tell you that you can use the program to capture a video of your screen if you want, or individual captures, as we’re doing. Multiple displays? It can handle that. The command flags I use are:
-m = Main monitor only
-C = Capture the cursor in addition to the screen
-x = Don’t play a sound when screen is captured
They combine on the command line like this:
$ screencapture -m -C -x savefile.png
This would capture just the main screen, including cursor, in file “savefile.png”, without making any sound. Try it!
On my MacBook Pro, the screen captures are about 3.5-4.0MB each, but they’ll add up if you record hundreds or thousands of images.
Eye-eyed readers will notice that I have used both “png” and “jpg” as filename suffixes. The program can automatically detect which is specified and use that format, so you can pick your favorite.
TURNING THE CAPTURE INTO A LOOPING SCRIPT
Now that we have a functional command, let’s set up a loop. At its most basic, that’s going to be:
for as many times as requested
sleep for a few seconds
capture the screen
end of loop
This isn’t quite how the command line interface shell works, of course, so the code’s going to look like this:
while [ $counter -lt $maxshots ] ; do sleep $sleeptime capture -m -C -x capture-${counter}.png counter=$(( counter + 1 )) done
Most of what’s happening here is tracking how many times we’ve looped. That’s done by counting up saved images with the “counter” variable, stopping when it’s not less than “maxshots”. The latter is a user specified variable, so if you want to take 100 pictures, you’d set it to maxshots=100. Notice that there’s also a sleep command in this loop that sleeps for “$sleeptime” seconds.
Want to cover your 45 minute lunch break by capturing the screen every 30 seconds? Then sleeptime should be set to 30 and maxshots should be 90 (two captures every minute for 45 minutes). Each resultant capture file will be saved with its sequential number too, so capture-4.png happens a minute prior to capture-6.png, and so on.
Shell scripts are best when they’re flexible, so instead of coding these values, we’re going to let the user specify them from the command line. Here’s my script:
#!/bin/sh # screencapture - use the Mac screencapture command to capture a sequence # of screenshots of the main window, in stealth mode. Handy if you're in # a questionable computing environment! capture="/usr/sbin/screencapture -x -m -C" sleeptime=60 # every 60 seconds maxshots=30 # default max screen captures while getopts "s:m:" opt; do case $opt in s ) sleeptime=$OPTARG; ;; m ) maxshots=$OPTARG; ;; ? ) echo "Usage: $0 [-s sleeptime] [-m maxcaps]" >&2 ; exit 1 esac done counter=0 while [ $counter -lt $maxshots ] ; do sleep $sleeptime $capture capture-${counter}.png counter=$(( counter + 1 )) done exit 0
A bit more complicated, but notice that the lower portion remains the while loop we talked about, just slightly tweaked. Everything else is trying to parse out what the user’s specified on the command line when invoking this shell script.
How’s it used? Like this:
$ ./watchscreen.sh -s 10 -m 30
Remember that this script is going to save all of the captures in the current directory, so move out of Desktop to remain maximally stealthy. Perhaps /tmp or similar? Your choice.
The above will take 30 captures, delaying 10 seconds between each one. This means that it’ll take 300 seconds or five minutes to complete. It will produce 30 image files:
The easiest way to view them is in the Finder, in gallery view. Here’s one of my actual captures:
You can see that capture 7 caught me looking at a fan on Amazon.com. It’s easy with the arrow key to quite quickly zip through the stack of images to see if anything’s actually happening. Don’t forget to delete them all when you’re done with your script too! Good luck.
Pro Tip: I’ve been writing about the Unix and Linux command line since before the release of MacOS. Please check out my extensive Linux help area and Linux shell script programming area for lots of additional tutorial content while you’re visiting. 99% of it also applies to MacOS!