Sending HTML mail using a shell script
Stefan Bogdanescu
Founder & Senior Architect
Sending HTML Emails Using a Shell Script: A Developer's Guide
As developers, we often need automation for tasks that bridge the gap between backend logic and user communication. While frameworks like Laravel handle complex application logic beautifully, sometimes you need a raw, system-level solution—a shell script—to trigger external actions, such as sending an email. The challenge lies in formatting the message correctly, especially when dealing with HTML, which requires proper MIME encoding.
This post will guide you through the practical steps and best practices for sending rich HTML emails using standard shell scripting tools.
The Challenge of HTML in Shell Scripting
Sending a simple text email via command-line utilities like mail or sendmail is straightforward. However, embedding complex HTML directly into these systems requires understanding MIME (Multipurpose Internet Mail Extensions) encoding. A raw HTML string will likely be misinterpreted by the mail server unless it is properly wrapped in the correct headers and boundaries so that the recipient's email client knows how to render the content as HTML rather than plain text.
Simply piping an HTML file into a mail command often results in corrupted formatting or plain text output, defeating the purpose of sending a visually rich message.
Method 1: The Robust Approach using mailx and MIME Structure
The most reliable way to send HTML emails from a shell script is to construct the entire email—including the necessary headers (MIME-Version, Content-Type, and the boundary marker for multipart messages)—within the script itself before piping the content to the mail utility. We will use mailx or a similar utility, which often provides more flexibility than basic mail.
Step-by-Step Implementation
Let’s assume you have an HTML file (report.html) and you want to send it via a system mail command.
1. Create the HTML Content: Ensure your HTML content is clean and ready for transmission.
<!-- report.html content -->
<html>
<head><title>Automated Report</title></head>
<body>
<h1>Daily System Report</h1>
<p>This report was generated automatically via a shell script.</p>
<p>Data integrity check passed successfully.</p>
</body>
</html>
2. The Shell Script Implementation: The script must generate the full MIME structure. We use a "multipart" message, separating the plain text headers from the actual HTML body.
#!/bin/bash
RECIPIENT="user@example.com"
SUBJECT="Automated HTML Report"
HTML_FILE="./report.html"
TEMP_EMAIL=$(mktemp)
# Construct the full MIME message structure
{
echo "From: System Bot <script@myserver.com>"
echo "To: $RECIPIENT"
echo "Subject: $SUBJECT"
echo "MIME-Version: 1.0"
echo "Content-Type: multipart/alternative; boundary=\"--BOUNDARY--\""
echo ""
echo "--BOUNDARY--"
echo "Content-Type: text/plain; charset=UTF-8"
echo "Content-Transfer-Encoding: 7bit"
echo ""
echo "Daily System Report:"
echo "Data integrity check passed successfully."
echo ""
echo "--BOUNDARY--"
echo "Content-Type: text/html; charset=UTF-8"
echo "Content-Transfer-Encoding: 7bit"
echo ""
cat "$HTML_FILE"
} > "$TEMP_EMAIL"
# Send the email using mailx (or sendmail)
mailx -s "$SUBJECT" "$RECIPIENT" < "$TEMP_EMAIL"
# Cleanup
rm "$TEMP_EMAIL"
echo "Email successfully sent to $RECIPIENT."
Developer Notes and Best Practices
- MIME is Key: The core of this process is correctly defining the
Content-Typeheaders and using a unique boundary string (e.g.,--BOUNDARY--). This tells the receiving mail server how to parse the message parts. - Temporary Files: Always use temporary files (
mktemp) for constructing complex output strings before sending them, as this improves script readability and error isolation. - Security: Never hardcode sensitive credentials directly into shell scripts if they are being sent over SMTP. Use environment variables or secure configuration management, similar to how you manage secrets in a Laravel application. For advanced scenarios involving SMTP authentication (which is often required for external delivery), consider using tools like
ssmtpor configuring your mail utility to use secure TLS connections.
Conclusion
Sending HTML emails via a shell script requires moving beyond simple text piping and embracing the complexity of MIME structuring. By manually scripting the necessary headers and boundaries, you gain complete control over how the email is rendered across different mail clients. While modern application development often favors using an established framework for email delivery (like Laravel's Mail facade), understanding these underlying system commands remains crucial for building robust automation pipelines. Mastering this level of low-level interaction makes you a much more versatile and powerful developer.