Hassle-Free Screenshots in Linux

Summary

"Damn it Jim, I'm a programmer, not a graphics designer." If you're firing up a big graphics program or clicking more than once to take a screenshot, you're doing too much.

Background

I take a lot of screenshots. I usually email them to people. It's a huge hassle to open up gimp, click around, take the screenshot, name the file, open a new email, attach the email, type the body, and send. "There has to be a better way."

Well kiddies, there is. Let this be a lesson to all of those who deprecate the command line.

My goal was one command that would let me click the window, type the text, and say who I wanted the screenshot sent to, and it'd be done. With the help of a few easy to install helper programs, it's easy to whip up a script to do just that.

Implementation

I wrote two scripts to perform the task at hand (see below):

  1. snapit.sh takes a screenshot and puts it into a file
  2. mailsnap.sh asks snapit.sh to take a screenshot, then mails it somewhere.

For these, you'll need:

  1. xwd (part of X11; you already have it if you're in run-level 5 right now)
  2. convert (part of ImageMagick; you probably already have this)
  3. mutt (a nice little mailer; I use PINE and I had mutt installed)

All you need to do is put snapit.sh and mailsnap.sh in your path, along with the helper programs above.

Usage


[bishop@predator ~]$ mailsnap.sh ripley@nostromo.weylandyutani.com ash@nostromo.weylandyutani.com
INFO: Click on the window you want to capture...
INFO: Type text to include in the body of the e-mail. Press Control-D when done.
Thought you might want to see this!

That's all folks. Run the program telling it whom you want to email, click the window you want to email, type any body text for the email, and you're done.

Extensions

If you need to take a lot of screenshots back to back (for example, you're putting together a manual), you can put these into a loop. Something like (this hasn't been tested):


#!/bin/bash
i=0
while :
do
    snapit.sh -o screenshot$i.png
    (( i += 1)) 
done

Thanks

Fog Creeks' FogBugz 4.0 inspired me.

Thanks also to Reid Thompson, who pointed out that my cavalier use of "function" in the original implementation broke pure Bourne shells, while remaining copasetic in Bourne-Again Shells. The code below should be portable.

The Code

Here you are, the code:


#!/bin/sh
# snapit.sh -- Take a snapshot of the screen
# Copyright (C) 2007 by ideacode, Inc.
# Mail comments or questions to the author, Bishop Bettini <bishop@ideacode.com>

DEFAULT_OUTPUT_FILE="screen.png"

usage() {
    cat >&2 <<EOTXT

Takes a snapshot of an interactively selected window or the whole desktop,
if so requested.  Sends the snapshot to a (possibly user-defined) file.

USAGE: `basename $0` [-r] [-o output file] [-X]
       `basename $0` [-h]
WHERE:
    -r  Take a picture of the whole desktop, not a selected window
    -o  Capture output into the named file. Default is "$DEFAULT_OUTPUT_FILE". Use "-" to send to stdout 
    -X  Overwrite output file, if it already exists
    -h  This help message

This program requires xwd (X11) and convert (ImageMagick) to be installed.

Possible Exit Codes:
0 - All is well
1 - Missing required helper program (xwd, convert, etc.)
2 - Unrecognized or bad argument
3 - Output file exists, but -X to overwrite wasn't given
EOTXT
}

# make sure we have everything we need
PROG_XWD=`which xwd`
PROG_CONVERT=`which convert`
if [ -z $PROG_XWD ]; then
    echo 'ERROR: xwd is not installed or in your PATH.' >&2
    usage
    exit 1
fi
if [ -z $PROG_CONVERT ]; then
    echo 'ERROR: convert is not installed or in your PATH.' >&2
    usage
    exit 1
fi
# default the program arguments
ARGS_XWD=''
ARGS_CONVERT=''
outfile=$DEFAULT_OUTPUT_FILE
overwrite=0
telltheusertoclick=1

# walk through the arguments
while [ 0 -lt $# ]; do
    # get the argument
    case $1 in
    -r) telltheusertoclick=0
        ARGS_XWD="-root $ARGS_XWD"
        ;;
    -o) shift
        outfile=$1
        ;;
    -X) overwrite=1
        ;;
    -h) usage
        exit 0
        ;;
    *) echo 'ERROR: Unrecognized argument' >&2
       usage
       exit 2
    esac

    # shift it away
    shift
done

# ignore overwrite requests if the file is stdout
if [ $outfile = "-" ]; then
    overwrite=0
fi

# check to see if the file already exists
if [ -e $outfile -a 0 -eq $overwrite ]; then
    echo "ERROR: Output file [$outfile] already exists. Force an overwrite with -X" >&2
    usage
    exit 3
fi

# do we need to tell the user anything?
if [ 1 -eq $telltheusertoclick ]; then
    echo "INFO: Click on the window you want to capture..."
fi

# go film
$PROG_XWD $ARGS_XWD | $PROG_CONVERT $ARGS_CONVERT - $outfile

exit 0


#!/bin/sh
# mailsnap.sh -- Take a snapshot of an interactively chosen window and e-mail it somewhere along with a description.
# Copyright (C) 2007 by ideacode, Inc.
# Mail comments or questions to the author, Bishop Bettini <bishop@ideacode.com>

usage() {
    cat >&2 <<EOTXT

Take a snapshot of an interactively chosen window and e-mail it as an
attachment somewhere.  You have the opportunity to put text in the e-mail body
as well.

USAGE: `basename $0` [-s "subject line of e-mail"] <e-mail address> [e-mail address [ ... ]]
       `basename $0` [-h]
WHERE:
    -h  This help message
    -s  The subject line of the e-mail, if you want one.  Remember to surround
        with quotation marks to protect spaces from the shell.

Run the program, specifying the e-mail addresses to which you want to send
the snapshot.  You will be prompted to type any particular comments to send
along with the snapshot, which will be found in the body of the e-mail.
The snapshot itself will be in PNG format and attached to the e-mail.

This program requires snapit.sh and mutt.

Possible Exit Codes:
0 - All is well
1 - Missing required helper program (snapit.sh, mutt, etc.)
2 - Unrecognized or bad argument
3 - Helper program failed
EOTXT    
}       

# make sure we have everything we need
PROG_SNAPIT=`which snapit.sh`
PROG_MUTT=`which mutt`
if [ -z $PROG_SNAPIT ]; then
    echo 'ERROR: snapit.sh is not installed or in your PATH.' >&2
    usage 
    exit 1
fi
if [ -z $PROG_MUTT ]; then
    echo 'ERROR: mutt is not installed or in your PATH.' >&2
    usage
    exit 1
fi

# default the program arguments
ARGS_SNAPIT=''
ARGS_MUTT=''
tmpsnap=/tmp/`basename $0`.$$.png
tmpbody=/tmp/`basename $0`.$$.txt
subject=''
addresses=''

# walk through the arguments
while [ 0 -lt $# ]; do
    # get the argument
    case $1 in
    -h) usage
        exit 0
        ;;
    -s) shift
        subject=$1
        ;;
    *) addresses="$1 $addresses"
       ;;
    esac

    # shift it away
    shift
done

# make sure we have addresses
if [ -z $addresses ]; then
    echo "ERROR: You must give at least one e-mail address" >&2
    usage
    exit 2
fi

# take the snapshot
$PROG_SNAPIT -o $tmpsnap
rc=$?
if [ $rc -ne 0 ]; then
    echo "ERROR: snapit.sh [$PROG_SNAPIT] failed (error code=[$rc])" >&2
    exit 3
fi

# make sure we have the file
if [ ! -r $tmpsnap ]; then
    echo "ERROR: snapit.sh [$PROG_SNAPIT] said it took the snapshot, but the snapshot has disappeared" >&2
    exit 3
fi

# let the user type some quick text (don't want to invoke the whole mutt apparatus
echo "INFO: Type text to include in the body of the e-mail. Press Control-D (once or twice) when done."
cat > $tmpbody

# mail it
$PROG_MUTT -s "$subject" -a $tmpsnap $addresses < $tmpbody
rc=$?
if [ $rc -ne 0 ]; then
    echo "ERROR: mutt [$PROG_MUTT] failed (error code=[$rc])" >&2
    exit 3
fi

# remove the file
rm -f $tmpsnap $tmpbody

exit 0