Tuesday, September 16, 2014

Sending e-mail using shell scripts


Most script programmers already know how easy it is to send e-mail from within shell scripts. A simple invocation of the mail program with the recipient for a command line argument will send all data read from standard input:
    $ mail root < /tmp/db_reorg.log
    $ echo "Coming home for dinner!" | mail sylvia@home.com
The "mail" command accepts additional command line parameters, e.g.
-s subjectUse subject for the e-mail title
-c addressSend copy of the mail to the specified address
-b addressSend blind carbon copy to specified address
Some common usage examples:
    Send a log file to the system administrator:
    # mail -s "Reorganization completed" root < db_reorg.log

    Send mail to "mark" with copy to "john":
    $ echo "John's coming with us" |
        mail -c john -s "Join us watching Star Wars" mark
Portability
Note that not all mail commands have an "-s" option to specify a mail subject. Sometimes an extended version of the mail command is available with the name "mailx", e.g./usr/ucb/mailx, or "Mail" (with a capital "M").
But how can we "attach" a file to a mail? We know that newer mail readers like Mozilla, Netscape Messenger or Microsoft Exchange have a way of displaying file attachments, e.g. images, ZIP files or audio files. How can we send some mail text (e.g. "See this picture of me surfing!") together with a binary file (e.g. "surfing.jpeg")?
The next chapter describes the traditional approach using "uuencode".


File attachments with "uuencode"

Sending binary files does not work well for the Internet. We could send a binary file "surfing.jpeg" using this command:
mail -s "picture of me surfing" sylvia@home.com < surfing.jpeg
but chances are high that the image will be unusable the time it arrives at the recipient.
During mail delivery the mail is relayed from one mail delivery agent to the next, until it finally arrives at the recipient. Each delivery agent may transform the mail message, e.g. by stripping the 8th bit of each character, removing NUL bytes (ASCII code 0), converting the end-of-line character LF ("line-feed", ASCII code 12) to a local representation (e.g. CR LF), or removing trailing space or TAB characters from each line.
Since only some characters are sure to arrive unmodified, the traditional solution is to encode the mail from binary format to a text format that is safe to transmit. The program used for this is called "uuencode" ("UNIX to UNIX encoding"), the program to decode the data is called "uudecode".
The simplest way to send a file as a mail attachment is shown by the following example:
    $ uuencode surfing.jpeg surfing.jpeg | mail sylvia@home.com
That's all! If Sylvia uses a current mail reader like Mozilla, Netscape Messenger or Microsoft Exchange, she will see a mail containing just one file attachment: the file "surfing.jpeg". In the command above we had to specify the file name two times: the first name denotes the input file to be encoded, and the second name is the file name the recipient will see.
This way we can include normal text, too:
    $ (cat mailtext; uuencode surfing.jpeg surfing.jpeg) |
        mail sylvia@home.com
Note: The file name "surfing.jpeg" again appears twice on the uuencode command line: the first time to specify the input file name, the second time for the remote extraction file name.
The first "cat" will prepend the contents of the file "mailtext" to the "uuencode"ed data of the image "surfing.jpeg". Both commands are executed within a sub shell, to combine the output of both commands to one output stream redirected to the "mail" command.
This method of sending file attachments works fairly well, but still has some shortcomings:
  • Many user agents cannot directly decode this kind of file attachments, e.g. "Eudora". Some mail agents explicitly require the decoding of the mail using the program "uudecode"
  • The "uuencode" encoding is not standardized. Text encoded on one system may not be decoded correctly by the recipient's system
        [the content of the mail message was saved to
        the file "mail.uue"]
        $ uudecode mail.uue
        The file "surfing.jpeg" was written to the current directory
The next method does not have this disadvantages. It works with most (newer) mail user agents and uses the "Multipurpose Internet Mail Extensions" (MIME).


File attachments with MIME

The MIME Standard ("Multipurpose Internet Mail Extensions") defines an impressive list of new features for e-mail messages, e.g.
  • arbitrary file attachments, e.g. images, audio files
  • more than one file attachment per mail
  • multi-part messages
Other features are non-ASCII character sets for message bodies and message headers.
But how can shell programmers use these features effectively?
At first there has to be a command-line driven program to send MIME mails. There are some MIME packages available free of charge, the most common being
But check your local system before downloading one of these packages, many UNIX systems (e.g. Linux) come with one or more of them pre-installed.
mutt is an interactive, text-oriented e-mail client. Not very useful for usage in a script, one might think. But on closer view the program turns out to provide an easy way to send e-mails with file attachments. Have a look at the following example:
    $ mutt -s "Birthday celebration" -a citymap.jpg all@friends.org \
     < invitation.txt
This command shows how to specify four important parts of an e-mail:
  • The recipient ("all@friends.org")
  • The main body of the e-mail (read from standard input, here redirected from the file "invitation.txt")
  • An attachment (with option -a)
  • A subject line (option -s)
The interesting part is the option -a, which allows us to send an attachment (in this case a picture of a city map). We could have added more attachments by using multiple -a options.
mutt always reads a text from standard input that will become the main body of the e-mail, the text before the attachment. If the mail should consist of attachments only, we can either specify/dev/null as the file to read, e.g.
    $ mutt -a syslogs.tar.gz admin@domain.org < /dev/null
or use an empty line as the mail body:
    $ echo | mutt -a syslogs.tar.gz admin@domain.org
mutt does some work behind the scenes, e.g. it's looking at the file extensions of the attachments, and selects an appropriate MIME type for each. This works reasonably well (depending on the configuration of the system in general and mutt in particular), but if more control about MIME types and the sending of the e-mail is required, the next package may be more appropriate.
The remainder of this chapter will use programs of the MetaMail package for the examples. Don't worry if the command lines seem somewhat cryptic, at the end of this article we will present a script simplifying the sending of mail attachments.
MetaMail provides two programs to send mail: mailto and metasend. We will use the second one for scripting, because it can be used non-interactively. The most interesting command line arguments of metasend are listed in the following table [refer to metasend(1) for a complete list].
-bbatch (non-interactive) mode
-c ccCC address (for copy of the mail)
-f filenameName of file to attach
-m MIME-typeMIME content type, e.g. "text/plain" or "application/octet-stream"
-s subjectTitle (subject) of the mail message
-t toRecipient's address
-nNext file attachment (needed for multiple file attachments)
-D descriptionDescription of the file content
-o outputfileName of an output file. If the -t option is given, the mail is sent directly to the addressee
This new knowledge enables us to send an audio file using "metasend":
    $ metasend -b -t john@friends.org -s "Hear our son!" \
     -m audio/basic -f crying.au
This invocation of "metasend" sends a mail to "john@friends.org" (option -t) with the subject "Hear our son!" (option -s), without prompting for further arguments (option -b). Without the "-b" argument, the command would have asked for addresses to send "carbon copies" to. The file to attach is called "crying.au", and the file type is "audio/basic".
The file type (or "MIME-type") "audio/basic" gives the recipient's mail reader a hint on the contents of the file attachment. Some contents may be displayed directly (e.g. "text/plain", "image/gif"), for others the mail reader may call external programs to present the contents (e.g. "application/postscript", "audio/wav"). The relation between MIME content type and external program to call often is established using a file called "mailcap" (see mailcap(4), if available on your system). Mozilla or Netscape's Communicator allows to specify "helper applications" that are invoked depending on the MIME type.
An annoying property of "metasend" is the need to specify the MIME type for each file. The script sendfile simplifies this. The following example sends two files, "cover.gif" and "contents.ps":
    $ sendfile editor@somewhere.com cover.gif contents.ps
    [Text to appear before the attachments, EOF to end]
    Hello John, you'll find a table of contents attached to
    this e-mail.
    ^D
The script is easier to use because it determines the MIME type of the specified files by examining their file name extension, e.g. ".gif" or ".ps".
The command used to determine the MIME type is getmimetype.

No comments: