Kit Roed
Kit Roed

Reputation: 5217

How do I send a file as an email attachment using Linux command line?

I've created a script that runs every night on my Linux server that uses mysqldump to back up each of my MySQL databases to .sql files and packages them together as a compressed .tar file. The next step I want to accomplish is to send that tar file through email to a remote email server for safekeeping. I've been able to send the raw script in the body an email by piping the backup text file to mailx like so:

$ cat mysqldbbackup.sql | mailx [email protected]

cat echoes the backup file's text which is piped into the mailx program with the recipient's email address passed as an argument.

While this accomplishes what I need, I think it could be one step better, Is there any way, using shell scripts or otherwise, to send the compressed .tar file to an outgoing email message as an attachment? This would beat having to deal with very long email messages which contain header data and often have word-wrapping issues etc.

Upvotes: 312

Views: 789956

Answers (25)

tripleee
tripleee

Reputation: 189809

There are several answers here suggesting mail or mailx so this is more of a background to help you interpret these in context. But there are some practical suggestions near the end.

Historical Notes

The origins of Unix mail go back into the mists of the early history of Bell Labs Unix™ (1969?), and we probably cannot hope to go into its full genealogy here. Suffice it to say that there are many programs which inherit code from or reimplement (or inherit code from a reimplementation of) mail and that there is no single code base which can be unambiguously identified as "the" mail.

However, one of the contenders to that position is certainly "Berkeley Mail" which was originally called Mail with an uppercase M in 2BSD (1978); but in 3BSD (1979), it replaced the lowercase mail command as well, leading to some new confusion. SVR3 (1986) included a derivative which was called mailx. The x was presumably added to make it unique and distinct; but this, too, has now been copied, reimplemented, and mutilated so that there is no single individual version which is definitive.

Back in the day, the de facto standard for sending binaries across electronic mail was uuencode. It still exists, but has numerous usability problems; if at all possible, you should send MIME attachments instead, unless you specifically strive to be able to communicate with the late 1980s.

MIME was introduced in the early 1990s to solve several problems with email, including support for various types of content other than plain text in a single character set which only really is suitable for a subset of English (and, we are told, Hawai'ian). This introduced support for multipart messages, internationalization, rich content types, etc, and quickly gained traction throughout the 1990s.

(The Heirloom mail/mailx history notes were most helpful when composing this, and are certainly worth a read if you're into that sort of thing.)

Current Offerings

As of 2018, Debian has three packages which include a mail or mailx command. (You can search for Provides: mailx.)

debian$ aptitude search ~Pmailx
i   bsd-mailx                       - simple mail user agent
p   heirloom-mailx                  - feature-rich BSD mail(1)
p   mailutils                       - GNU mailutils utilities for handling mail

(I'm not singling out Debian as a recommendation; it's what I use, so I am familiar with it; and it provides a means of distinguishing the various alternatives unambiguously by referring to their respective package names. It is obviously also the distro from which Ubuntu gets these packages.)

  • bsd-mailx is a relatively simple mailx which does not appear to support sending MIME attachments. See its manual page and note that this is the one you would expect to find on a *BSD system, including MacOS, by default.
  • heirloom-mailx is now being called s-nail and does support sending MIME attachments with -a. See its manual page and more generally the Heirloom project
  • mailutils aka GNU Mailutils includes a mail/mailx compatibility wrapper which does support sending MIME attachments with -A

With these concerns, if you need your code to be portable and can depend on a somewhat complex package, the simple way to portably send MIME attachments is to use mutt.

If you know what you are doing, you can assemble an arbitrary MIME structure with the help of echo and base64 and e.g. qprint (or homegrown replacements; both base64 and qprint can easily be implemented as Perl one-liners) and pipe it to sendmail; but as several other answers on this page vividly illustrate, you probably don't.

( printf '%s\n' \
    "From: myself <[email protected]>" \
    "To: backup address <[email protected]>" \
    "Subject: Backup of $(date)" \
    "MIME-Version: 1.0" \
    "Content-type: application/octet-stream; filename=\"mysqldbbackup.sql\"" \
    "Content-transfer-encoding: base64" \
    ""
  base64 < mysqldbbackup.sql ) |
sendmail -oi -t

This assumes that sendmail is on your PATH; sometimes it's not (and of course, sometimes it's simply not installed at all). Look in /usr/lib, /usr/sbin, /usr/libexec or etc; or query your package manager. Once you find it, you might want to augment your PATH in the script, or hardcode the full pathname to sendmail (and ditto for any other nonstandard binaries which may or may not be installed on your system).

This still does not attempt to provide any solution for situations where you need to send non-ASCII Unicode text or lines longer than what SMTP allows, etc etc etc. For a robust solution, I would turn to an existing tool like mutt, or a modern scripting language like Python; https://docs.python.org/3/library/email.examples.html has examples for many common use cases.

Upvotes: 11

Girdhar Singh Rathore
Girdhar Singh Rathore

Reputation: 5615

using mailx command

 echo "Message Body Here" | mailx -s "Subject Here" -a file_name [email protected]

using sendmail

#!/bin/ksh

fileToAttach=data.txt

`(echo "To: [email protected]"
  echo "Cc: [email protected]"
  echo "From: Application"
  echo "Subject: your subject"
  echo  your body
  uuencode $fileToAttach $fileToAttach
  )| eval /usr/sbin/sendmail -t `;

Upvotes: 1

user1651561
user1651561

Reputation: 197

Send a Plaintext body email with one plaintext attachment with mailx:

(
  /usr/bin/uuencode attachfile.txt myattachedfilename.txt; 
  /usr/bin/echo "Body of text"
) | mailx -s 'Subject' [email protected]

Below is the same command as above, without the newlines

( /usr/bin/uuencode /home/el/attachfile.txt myattachedfilename.txt; /usr/bin/echo "Body of text" ) | mailx -s 'Subject' [email protected]

Make sure you have a file /home/el/attachfile.txt defined with this contents:

<html><body>
Government discriminates against programmers with cruel/unusual 35 year prison
sentences for making the world's information free, while bankers that pilfer 
trillions in citizens assets through systematic inflation get the nod and 
walk free among us.
</body></html>

If you don't have uuencode read this: https://unix.stackexchange.com/questions/16277/how-do-i-get-uuencode-to-work

On Linux, Send HTML body email with a PDF attachment with sendmail:

Make sure you have ksh installed: yum info ksh

Make sure you have sendmail installed and configured.

Make sure you have uuencode installed and available: https://unix.stackexchange.com/questions/16277/how-do-i-get-uuencode-to-work

Make a new file called test.sh and put it in your home directory: /home/el

Put the following code in test.sh:

#!/usr/bin/ksh
export MAILFROM="[email protected]"
export MAILTO="[email protected]"
export SUBJECT="Test PDF for Email"
export BODY="/home/el/email_body.htm"
export ATTACH="/home/el/pdf-test.pdf"
export MAILPART=`uuidgen` ## Generates Unique ID
export MAILPART_BODY=`uuidgen` ## Generates Unique ID

(
 echo "From: $MAILFROM"
 echo "To: $MAILTO"
 echo "Subject: $SUBJECT"
 echo "MIME-Version: 1.0"
 echo "Content-Type: multipart/mixed; boundary=\"$MAILPART\""
 echo ""
 echo "--$MAILPART"
 echo "Content-Type: multipart/alternative; boundary=\"$MAILPART_BODY\""
 echo ""
 echo "--$MAILPART_BODY"
 echo "Content-Type: text/plain; charset=ISO-8859-1"
 echo "You need to enable HTML option for email"
 echo "--$MAILPART_BODY"
 echo "Content-Type: text/html; charset=ISO-8859-1"
 echo "Content-Disposition: inline"
 cat $BODY
 echo "--$MAILPART_BODY--"

 echo "--$MAILPART"
 echo 'Content-Type: application/pdf; name="'$(basename $ATTACH)'"'
 echo "Content-Transfer-Encoding: uuencode"
 echo 'Content-Disposition: attachment; filename="'$(basename $ATTACH)'"'
 echo ""
 uuencode $ATTACH $(basename $ATTACH)
 echo "--$MAILPART--"
) | /usr/sbin/sendmail $MAILTO

Change the export variables on the top of test.sh to reflect your address and filenames.

Download a test pdf document and put it in /home/el called pdf-test.pdf

Make a file called /home/el/email_body.htm and put this line in it:

<html><body><b>this is some bold text</b></body></html>

Make sure the pdf file has sufficient 755 permissions.

Run the script ./test.sh

Check your email inbox, the text should be in HTML format and the pdf file automatically interpreted as a binary file. Take care not to use this function more than say 15 times in a day, even if you send the emails to yourself, spam filters in gmail can blacklist a domain spewing emails without giving you an option to let them through. And you'll find this no longer works, or it only lets through the attachment, or the email doesn't come through at all. If you have to do a lot of testing on this, spread them out over days or you'll be labelled a spammer and this function won't work any more.

Upvotes: 11

David Schlosnagle
David Schlosnagle

Reputation: 4333

You can use mutt to send the email with attachment

mutt -s "Backup" -a mysqldbbackup.sql [email protected] < message.txt

Upvotes: 13

Chris N
Chris N

Reputation: 627

From looking at man mailx, the mailx program does not have an option for attaching a file. You could use another program such as mutt.

echo "This is the message body" | mutt -a file.to.attach -s "subject of message" [email protected]

Command line options for mutt can be shown with mutt -h.

Upvotes: 41

Nathan Fellman
Nathan Fellman

Reputation: 127598

Depending on your version of Linux it may be called mail. To quote @David above:

mail -s "Backup" -a mysqldbbackup.sql [email protected] < message.txt

or also:

cat message.txt | mail -s "Backup" -a mysqldbbackup.sql [email protected]

Upvotes: 54

rynop
rynop

Reputation: 53689

None of the mutt ones worked for me. It was thinking the email address was part of the attachment. Had to do:

echo "This is the message body" | mutt -a "/path/to/file.to.attach" -s "subject of message" -- [email protected]

Upvotes: 309

Sourabh Potnis
Sourabh Potnis

Reputation: 1471

 echo -e 'Hi, \n These are contents of my mail. \n Thanks' | mailx -s 'This is my email subject' -a /path/to/attachment_file.log -b [email protected] -c [email protected] -r [email protected] [email protected] [email protected] [email protected]

Upvotes: 24

dagorv
dagorv

Reputation: 9

This is how I am doing with one large log file in CentOS:

#!/bin/sh
MAIL_CMD="$(which mail)"
WHOAMI="$(whoami)"
HOSTNAME="$(hostname)"
EMAIL"[email protected]"
LOGDIR="/var/log/aide"
LOGNAME="$(basename "$0")_$(date "+%Y%m%d_%H%M")"

if cd ${LOGDIR}; then
  /bin/tar -zcvf "${LOGDIR}/${LOGNAME}".tgz "${LOGDIR}/${LOGNAME}.log" > /dev/null 2>&1
  if [ -n "${MAIL_CMD}" ]; then
  # This works too. The message content will be taken from text file below
  # echo 'Hello!' >/root/scripts/audit_check.sh.txt
  # echo "Attachment" | ${MAIL_CMD} -s "${HOSTNAME} Aide report" -q /root/scripts/audit_check.sh.txt -a ${LOGNAME}.tgz -S from=${WHOAMI}@${HOSTNAME} ${EMAIL}
    echo "Attachment" | ${MAIL_CMD} -s "${HOSTNAME} Aide report" -a "${LOGNAME}.tgz" -S from="${WHOAMI}@${HOSTNAME}" "${EMAIL}"
    /bin/rm "${LOGDIR}/${LOGNAME}.log"
  fi
fi

Upvotes: 0

rumpel
rumpel

Reputation: 8308

Mailutils makes this a piece of cake

echo "Body" | mail.mailutils -M -s "My Subject" -A attachment.pdf [email protected]
  • -A file attaches a file
  • -M enables MIME, so that you can have an attachment and plaintext body.

If not yet installed, run

sudo apt install mailutils

Upvotes: 3

Alexander Yancharuk
Alexander Yancharuk

Reputation: 14531

Another alternative - Swaks (Swiss Army Knife for SMTP).

swaks -tls \
    --to ${MAIL_TO} \
    --from ${MAIL_FROM} \
    --server ${MAIL_SERVER} \
    --auth LOGIN \
    --auth-user ${MAIL_USER} \
    --auth-password ${MAIL_PASSWORD} \
    --header "Subject: $MAIL_SUBJECT" \
    --header "Content-Type: text/html; charset=UTF-8" \
    --body "$MESSAGE" \
    --attach mysqldbbackup.sql

Upvotes: 6

Paras Singh
Paras Singh

Reputation: 403

If mutt is not working or not installed,try this-

*#!/bin/sh

FilePath=$1
FileName=$2
Message=$3
MailList=$4

cd $FilePath

Rec_count=$(wc -l < $FileName)
if [ $Rec_count -gt 0 ]
then
(echo "The attachment contains $Message" ; uuencode $FileName $FileName.csv ) | mailx -s "$Message" $MailList
fi*

Upvotes: -1

nurp
nurp

Reputation: 1275

If the file is text, you can send it easiest in the body as:

sendmail [email protected] < message.txt

Upvotes: 0

Konchog
Konchog

Reputation: 2200

From source machine

mysqldump --defaults-extra-file=sql.cnf database | gzip | base64 | mail [email protected]

On Destination machine. Save the received mail body as db.sql.gz.b64; then..

base64 -D -i db.sql.gz.b64 | gzip -d | mysql --defaults-extra-file=sql.cnf

Upvotes: 1

Fredrik Wendt
Fredrik Wendt

Reputation: 1148

I use SendEmail, which was created for this scenario. It's packaged for Ubuntu so I assume it's available

sendemail -f [email protected] -t [email protected] -m "Here are your files!" -a file1.jpg file2.zip

http://caspian.dotconf.net/menu/Software/SendEmail/

Upvotes: 33

poncho
poncho

Reputation: 19

I used

echo "Start of Body" && uuencode log.cfg readme.txt | mail -s "subject" "[email protected]" 

and this worked well for me....

Upvotes: 1

Pipo
Pipo

Reputation: 5093

the shortest way for me is

file=filename_or_filepath;uuencode $file $file|mail -s "optional subject" email_address

so for your example it'll be

file=your_sql.log;gzip -c $file;uuencode ${file}.gz ${file}|mail -s "file with magnets" [email protected]

the good part is that I can recall it with Ctrl+r to send another file...

Upvotes: 0

Yoav
Yoav

Reputation: 6098

Not a method for sending email, but you can use an online Git server (e.g. Bitbucket or a similar service) for that.

This way, you can use git push commands, and all versions will be stored in a compressed and organized way.

Upvotes: 0

Alejandro Santillan
Alejandro Santillan

Reputation: 11

I usually only use the mail command on RHEL. I have tried mailx and it is pretty efficient.

mailx -s "Sending Files" -a First_LocalConfig.conf -a
Second_LocalConfig.conf [email protected]

This is the content of my msg.

.

Upvotes: 1

Allan Pinto
Allan Pinto

Reputation: 144

mailx does have a -a option now for attachments.

Upvotes: 0

Mike Graf
Mike Graf

Reputation: 5317

Just to add my 2 cents, I'd write my own PHP Script:

http://php.net/manual/en/function.mail.php

There are lots of ways to do the attachment in the examples on that page.

Upvotes: 0

glenn jackman
glenn jackman

Reputation: 247142

I once wrote this function for ksh on Solaris (uses Perl for base64 encoding):

# usage: email_attachment to cc subject body attachment_filename
email_attachment() {
    to="$1"
    cc="$2"
    subject="$3"
    body="$4"
    filename="${5:-''}"
    boundary="_====_blah_====_$(date +%Y%m%d%H%M%S)_====_"
    {
        print -- "To: $to"
        print -- "Cc: $cc"
        print -- "Subject: $subject"
        print -- "Content-Type: multipart/mixed; boundary=\"$boundary\""
        print -- "Mime-Version: 1.0"
        print -- ""
        print -- "This is a multi-part message in MIME format."
        print -- ""
        print -- "--$boundary"
        print -- "Content-Type: text/plain; charset=ISO-8859-1"
        print -- ""
        print -- "$body"
        print -- ""
        if [[ -n "$filename" && -f "$filename" && -r "$filename" ]]; then
            print -- "--$boundary"
            print -- "Content-Transfer-Encoding: base64"
            print -- "Content-Type: application/octet-stream; name=$filename"
            print -- "Content-Disposition: attachment; filename=$filename"
            print -- ""
            print -- "$(perl -MMIME::Base64 -e 'open F, shift; @lines=<F>; close F; print MIME::Base64::encode(join(q{}, @lines))' $filename)"
            print -- ""
        fi
        print -- "--${boundary}--"
    } | /usr/lib/sendmail -oi -t
}

Upvotes: 15

I use mpack.

mpack -s subject file [email protected]

Unfortunately mpack does not recognize '-' as an alias for stdin. But the following work, and can easily be wrapped in an (shell) alias or a script:

mpack -s subject /dev/stdin [email protected] < file

Upvotes: 29

Gunstick
Gunstick

Reputation: 431

metamail has the tool metasend

metasend -f mysqlbackup.sql.gz -t [email protected] -s Backup -m application/x-gzip -b

Upvotes: 2

Daniel Fone
Daniel Fone

Reputation: 2905

Or, failing mutt:

gzip -c mysqldbbackup.sql | uuencode mysqldbbackup.sql.gz  | mail -s "MySQL DB" [email protected]

Upvotes: 81

Related Questions