This article was published in 2014 under the title “Checking Mail with mail on Linux,” but its body mixed local mailbox reading, a test send, and multi-user terminal messaging. This maintained edition addresses one narrower question: how to send one authorized email safely and auditably from the Linux command line. Local mailbox reading, IMAP/POP3 retrieval, and write terminal messaging are separate topics; their commands are not interchangeable.
The boundary comes first: use this guide only with sending accounts, domains, hosts, and recipients that you own or are authorized to use. It does not show how to create an open relay, forge senders, evade anti-abuse controls, send unsolicited bulk mail, or hide origin. Never disable TLS certificate checks, place a password in an argument or URL, or point a production queue at unconsenting recipients just to “make it work.”
The complete visible body from
source_exportis preserved at the end. Old email addresses and message identifiers in the archive are narrowly redacted; old HTTP links remain provenance, not current recommendations.
Table of Contents
Choose the right submission path first
| Path | Suitable use | Trust boundary | Not suitable for |
|---|---|---|---|
| Local MTA sendmail-compatible interface | Administrator-managed servers, system notifications, an existing queue and bounce process | The application hands the message to a local Postfix, Exim, Sendmail, or compatible implementation; that MTA delivers it | Improvised public SMTP on a personal host or bypassing organizational policy |
| Authenticated SMTP submission | Single-user scripts and desktop/server clients where the provider documents a submission host | The client validates TLS and authenticates to a provider; the provider performs onward delivery | Direct delivery to arbitrary recipient MX hosts on port 25 or shared long-lived passwords |
| Provider mail API | Applications that need structured responses, idempotency keys, templates, or event status | The application holds a least-scope API credential and follows provider data and retry rules | Invented generic endpoints, tokens in command lines, or quota evasion |
mail/mailx names a family of mail user agents whose implementations and options differ by distribution. sendmail is often only a compatibility interface and does not identify the MTA behind it. Confirm the system owner, package source, and responsible operator before inferring configuration from a binary name.
Record provider facts before changing anything
Record the following from the organization administrator or the provider's current official documentation, with a review date:
- the submission hostname and the exact 587/STARTTLS or 465/implicit-TLS combination;
- the supported authentication mechanism: OAuth 2.0, a short-lived token, an app password, or another organization-approved method;
- verified
From:identities, allowed envelope-from/Return-Path domains, and who signs DKIM; - message-size, rate, concurrency, daily-quota, recipient-count, and retry limits;
- where bounces, complaints, suppression lists, logs, and content are kept, and for how long;
- API region, version, scopes, idempotency, and revocation procedure if an API is used.
An ordinary account password may not be accepted for SMTP AUTH, and enabling multi-factor authentication does not imply that the sign-in password can be reused. OAuth and app-password availability depends on provider and organization policy. Stop if the official parameters, sending authority, or a controlled test inbox are unavailable.
Distinguish the envelope from message headers
The SMTP envelope and the headers visible to a reader are separate layers. RFC 5321 defines the transport envelope and RFC 5322 defines message format; neither is automatically equal to the other.
| Field | Purpose | Security requirement |
|---|---|---|
| envelope MAIL FROM | Bounce address; its domain may participate in SPF/DMARC | Use a provider-approved identity with a working bounce process; do not improvise it in scripts |
| envelope RCPT TO | The actual delivery destination | Accept only a fixed allowlist or validated business data; reject newline injection |
From: | Author identity shown to the reader and source of the DMARC Author Domain | Use an authorized identity aligned with the provider's SPF/DKIM design |
To: / Cc: | Visible display recipients | They need not equal envelope recipients; never expose other people's addresses |
Bcc: | Delivered through the envelope but absent from the final header | Verify that the client removes it; use a compliant list system rather than a large Bcc for bulk mail |
Message-ID: / Date: | Deduplication, threading, and diagnosis | Let a trusted client/MTA generate them and redact them before sharing logs |
Never concatenate untrusted text into headers; CR/LF characters can create extra headers or recipients. Every example here uses the RFC-reserved .invalid domain and therefore cannot deliver as written.
Path one: an existing local MTA’s sendmail interface
Begin with read-only inventory:
command -v sendmail
command -v msmtp
msmtp --version
If /usr/sbin/sendmail exists, determine which maintained package owns it, who administers it, which interfaces it listens on, whether authentication and relay restrictions are correct, and who owns queues and bounces. An application can commonly submit a reviewed message to the local queue through a compatible call like this:
/usr/sbin/sendmail -t -oi < "$HOME/mail-test.eml"
-t commonly extracts recipients from message headers, while -oi prevents a body line containing only a dot from ending input early; verify the final semantics in the installed implementation's manual. A successful exit means the local MTA accepted the message, not that a remote server delivered it. Do not expose a listener to the Internet, create an unauthenticated relay, broaden mynetworks/ACLs, or use -f to impersonate a domain. If there is no administrator-approved queue, bounce, and logging process, use the organization's submission service or API.
Path two: authenticated SMTP submission
RFC 6409 separates message submission from server-to-server relay. Ordinary clients should use the provider's submission service, not attempt direct delivery to a recipient MX on port 25. RFC 8314 recognizes two secure deployments:
| Mode | Common port | When TLS begins | What must be verified |
|---|---|---|---|
| STARTTLS submission | 587 | SMTP starts first and upgrades through STARTTLS; stop if the upgrade fails | Hostname, certificate chain, validity, successful STARTTLS, and only then authentication |
| Implicit-TLS submission | 465 | TLS begins immediately after TCP connection | Hostname, certificate chain, and validity before authentication |
Ports are conventions; the provider's official configuration is authoritative. A bad system clock or CA store can also break validation. Repair the cause—never use trust-all, tls_certcheck off, ignored hostnames, or an unreviewed pinned certificate fingerprint.
You can probe TLS with the provider-documented hostname without sending credentials or a message. First confirm that the local OpenSSL version supports these options:
openssl s_client -starttls smtp -connect smtp.example.invalid:587 -servername smtp.example.invalid -verify_hostname smtp.example.invalid -verify_return_error -brief </dev/null
openssl s_client -connect smtp.example.invalid:465 -servername smtp.example.invalid -verify_hostname smtp.example.invalid -verify_return_error -brief </dev/null
A successful probe proves only that the network/TLS path worked at that moment; it does not prove account authentication, sender authorization, or final delivery. Stop on a hostname mismatch, unknown CA, downgrade or captive-proxy page, missing STARTTLS on 587, or an unexpected host.
Keep non-secret settings in an isolated msmtp configuration
msmtp is a maintained SMTP client with a sendmail-compatible interface. Install it from a supported distribution package and check its version, then create an isolated file that cannot overwrite a default account:
ConfigDir="$HOME/.config/msmtp"
ConfigFile="$ConfigDir/config-controlled"
test ! -L "$ConfigDir" || { echo "Stop: $ConfigDir is a symlink"; exit 1; }
test ! -e "$ConfigFile" && test ! -L "$ConfigFile" || { echo "Stop: $ConfigFile already exists"; exit 1; }
umask 077
install -d -m 700 -- "$ConfigDir"
install -m 600 /dev/null "$ConfigFile"
Confirm that both directory and file belong to the current user. Enter this 587/STARTTLS template in an editor; every value remains a non-deliverable placeholder:
account controlled
host smtp.example.invalid
port 587
auth on
user sender@example.invalid
from sender@example.invalid
allow_from_override off
tls on
tls_starttls on
tls_trust_file system
set_from_header on
set_date_header auto
set_msgid_header auto
remove_bcc_headers on
This template targets the official msmtp 1.8.34 manual. If the official manual for the installed version does not list set_msgid_header, do not guess or ignore the error: upgrade through a supported package, or have a reviewed MUA/provider generate Message-ID:.
If and only if the provider requires 465 with implicit TLS, change these two lines:
port 465
tls_starttls off
In msmtp, tls_starttls off together with tls on means that the connection is inside TLS from its beginning; it does not disable encryption. Keep default certificate verification and never add tls_certcheck off.
The configuration deliberately has no password. For an interactive test, let the client use a supported system keyring or a secure TTY prompt. For unattended operation, follow the provider and official msmtp documentation to integrate an audited OAuth-token helper, keyring, or passwordeval. msmtp may also look for a legacy ~/.netrc; confirm that the test account has no unintended match, without printing that file. The helper must have least privilege and must not expose secrets through stdout beyond the expected value, logs, environment snapshots, or temporary files. Never place a password/token in argv, a URL, shell history, a repository, a .netrc example, or a screenshot.
Expand the effective configuration without sending. --pretend prints asterisks instead of the password, but its output can still contain identities, hosts, and paths, so redact it before sharing:
msmtp --file="$HOME/.config/msmtp/config-controlled" --account=controlled --pretend
Do not use --debug with a real account: the official manual warns that a complete conversation can expose password material in an easily decodable form.
Build a minimal message and use a controlled sink first
Save the following as $HOME/mail-test.eml with mode 600. Keep the .invalid values initially and omit attachments, personal data, production templates, real Bcc addresses, and tracking pixels:
From: sender@example.invalid
To: controlled-recipient@example.invalid
Subject: controlled submission test
Auto-Submitted: auto-generated
This is an authorized delivery test. No reply is required.
For stage one, point an isolated test account at an organization-approved development SMTP sink or sandbox. The sink must bind only to loopback or a controlled test network, must not relay to the public Internet, and should retain data briefly. Confirm that it captures exactly one message with correct headers and no Bcc or secret, then clear its test data. --pretend checks only the client configuration it would use; it does not replace a sink test.
Only in stage two replace the host, sender, and sole recipient with values you control and the provider has approved. Review the diff line by line, then send once:
msmtp --file="$HOME/.config/msmtp/config-controlled" --account=controlled --read-recipients < "$HOME/mail-test.eml"
This configuration locks the envelope-from and reads envelope recipients from message headers. Do not pass unvalidated addresses as positional arguments, and do not make the first send from a loop, scheduler, or retry worker. An interactive credential prompt must come from the reviewed local client, not a web popup or unknown script.
Path three: a provider API
An API can suit applications that need structured submission IDs, idempotency, or event callbacks, but it is not a route around mail policy. Use only the provider's current official HTTPS endpoint, SDK, and certificate validation. Keep the token in OS secret storage or a read-only runtime mount, scope it to one sending service/domain and the necessary actions, and define rotation and expiry.
Do not copy a “generic curl” example and put a Bearer token in argv or the environment. Request bodies, failure responses, proxies, and APM can also log recipients and message content. Begin in the provider's sandbox or validation mode; keep the first production request to one controlled recipient and one unique idempotency key. Retry temporary errors only with the provider's documented bounded backoff rules; never blindly replay permanent rejection, authorization, or quota errors.
SPF, DKIM, and DMARC belong to the domain owner
- SPF authorizes infrastructure to send for the envelope MAIL FROM/HELO domain; a command-line client cannot “pass SPF” by adding a header.
- DKIM is added by a trusted MTA/provider using a domain key; private keys do not belong in ordinary scripts or workstations.
- DMARC uses the visible
From:Author Domain and requires alignment with a passing SPF or DKIM identifier. Current RFC 9989 obsoletes RFC 7489. A pass proves authorized domain use, not safe content or guaranteed inbox placement.
Only the domain owner or an explicit delegate may modify DNS records, selectors, reporting addresses, and enforcement policy. Inventory every legitimate mail stream, complete provider verification, and observe reports before staged policy changes; do not copy someone else's TXT record or jump directly to a rejection policy. DMARC reports and bounces can contain addresses, IPs, headers, or message excerpts, so restrict access and retention.
Delivery evidence, bounces, and privacy
| Evidence | What it proves | What it does not prove |
|---|---|---|
| Client exit code/submission ID | The client or submission/API accepted the request | Final recipient server acceptance or inbox placement |
| Local queue ID | The local MTA queued it | Eventual queue success or authentication pass |
Remote SMTP 250/provider delivered event | A remote stage accepted the message | Reading, trustworthy content, or no later bounce |
Received and Authentication-Results in inbox source | The test message's real path and SPF/DKIM/DMARC results | Identical behavior at every provider or for later messages |
| DSN/bounce/complaint event | A particular failure or user signal | Permission to retry forever or keep contacting the address |
Record only the test time, configuration version, submission/queue ID, redacted status code, and final outcome. Do not retain full content, OAuth tokens, AUTH transcripts, complete recipient lists, or unredacted raw headers. Someone must own the bounce address; hard bounces, complaints, unsubscribes, and explicit refusals belong in suppression, not automatic resubmission.
Provider 4xx responses usually denote a temporary condition and 5xx a permanent rejection, but follow provider documentation and enhanced status codes. Retries need bounded attempts/time, jitter, and deduplication. If acceptance of the original request is uncertain, query by submission ID before risking a duplicate.
Common failures and stop conditions
| Symptom | Evidence to inspect first | Next step or stop condition |
|---|---|---|
| TLS hostname/chain/validity failure | Hostname, system clock, CA updates, proxy | Repair the source of truth; never disable validation |
| No STARTTLS on 587 | Official port, EHLO capabilities, interception | Stop authentication; never send a secret over plaintext |
535/authentication failure | Authentication type, account policy, token scope/expiry | Do not guess repeatedly; revoke exposed credentials and reissue officially |
550 sender or recipient rejected | Verified identity, envelope/header, enhanced status | Do not forge From: or rotate domains to evade policy |
| Local command succeeds but mail is absent | Exact queue ID, MTA log, bounce route | Have the administrator inspect that record; do not flush the whole queue |
| SPF/DKIM/DMARC failure | Inbox source, DNS, signing domain, alignment | Domain/provider owner fixes it; do not forge authentication headers locally |
| Quota exceeded or many 4xx responses | Provider limits, queue depth, duplicate keys | Pause producers; do not raise concurrency to evade limits |
| Recipient/content origin is unclear | Authority, consent, data source, retention basis | Do not send until an owner confirms it |
Never use tls_certcheck off, --insecure/trust-all, plaintext AUTH, password arguments, an open relay, sender spoofing, purchased/scraped lists, unsolicited bulk mail, or disabled provider anti-abuse, unsubscribe, or suppression controls.
Rollback, revocation, and offboarding
- Before the first live test, retain a redacted configuration diff, owner, test address, rate ceiling, and rollback window.
- On an anomaly, stop the scheduler/queue producer from creating new mail first; do not indiscriminately delete other applications' queues.
- Act only on exact submission/queue IDs for this test, and determine whether already accepted mail can still arrive.
- Restore the prior verified configuration, revoke or rotate the dedicated token/app password, and remove the temporary message and sink data.
- When retiring an account, remove keyring/secret mounts and service permissions, revoke API/OAuth credentials, and check whether other streams still use the DKIM selector, DNS authorization, webhook, or bounce address before changing them.
- Finally verify that no orphan scheduler, public listener, unhandled bounce, queued backlog, or long-lived sensitive log remains.
Acceptance checklist for one controlled email
- The sending account, domain, host, and sole test recipient are explicitly authorized.
- Current official provider documentation supplied the host, port, TLS, authentication, sender identity, quota, and bounce process.
- Local MTA, authenticated submission, and API paths are distinguished; there is no arbitrary-MX direct delivery or open relay.
- TLS hostname and certificate validation succeeds; credentials are used only after encryption.
- No password/token is present in argv, URLs, repositories, messages, logs, screenshots, or history.
- envelope-from,
From:, recipients, and SPF/DKIM/DMARC ownership are aligned. - A local/provider sink passed; production sends exactly one message to a controlled address.
- Redacted submission ID, remote result, and authentication-header evidence is retained, with “accepted” distinguished from inbox/reading.
- Rate, retry, bounce, complaint, unsubscribe, suppression, and privacy retention have owners.
- Stop, exact rollback, credential revocation, and offboarding have been tested.
References
- RFC 6409: Message Submission for Mail
- RFC 8314: TLS for mail submission and access
- RFC 5321: SMTP and the transport envelope
- RFC 5322: Internet Message Format
- RFC 7628: SASL mechanisms for OAuth
- Official msmtp 1.8.34 manual
- Official Postfix `sendmail(1)` compatibility-interface manual
- Official OpenSSL `s_client` manual
- RFC 7208: SPF
- RFC 6376: DKIM
- RFC 9989: DMARC (obsoletes RFC 7489)
Historical source archive
The following is the complete inert archive of the visible 2014 body in source_export. Six narrow replacements avoid re-exposing identifiable old email and per-message identifiers: one mailbox-path username, one mailbox-list email, one local identity, two email addresses, and one Message-ID. General tutorial mentions of frank/renee, historical domains, and two old HTTP source links remain unchanged; the links are provenance only, not current service, download, or configuration advice. The source had no trailing whitespace, and nothing else was changed. Do not execute any command, address, or practice inside the fence.
Table of Contents
Toggle
- [Linux下mail使用技巧](https://blog.lazying.art/en/html/computer_internet/unix_linux/command_shell_software/1143/linux%e4%b8%8b%e9%82%ae%e4%bb%b6mail%e6%9f%a5%e6%94%b6.html/#Linux%E4%B8%8Bmail%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7)
# Linux下mail使用技巧
登录LINUX系统后,经常会看到”you have mail”,却苦于不知道如何查看,相信菜鸟们都遇到过,偶在网上用“linux mail”找了很久,但大都是介绍[mail服务器](http://earnfs.sinaapp.com/html/1141.htm)的,黄天总算没负有心人,在洪恩在找到一篇介绍基础的文章,不敢独享。
系统提供了用户之间通信的邮件系统,当用户打开终端注册登录时发现系统给出如下信息:
you have mail.
这时用户可通过键入mail命令读取信件:
$ mail
mail程序将逐个显示用户的信件,并依照时间顺序,显示最新的信件。每显示一段信件,mail都询问用户是否要对该信件作些处理。若用户回答d,则表示 删除信件;若仅按回车键,表示对信件不作任何改动(信件仍旧保存,下次还可读这一信件);若回答p,则要求重复显示信件;s filename表示要把信件存入所命名的文件;若回答q,表示要从mail退出。
我们在本章的第一个例子中演示了如何写一封信,作为练习,你可送信件给自己,然后键入mail读取自己发的信件,看看会有什么效果。(发信给自己是一种设置备忘录的方法)。
$mail frank 给自己写信
subject: test
This is a mail test
CRL-d
EOT
$
$mail 查看信件
“/var/spool/mail/[historical user redacted]:”1 message 1 new
>N[historical email redacted]Thu Mar 25 11:00 13/403 “test”
&
Message 1:
From frank Thu Mar 25 11:00:25 1999/3/25
Received: ([historical local identity redacted])
by xteam.xteamlinux.com(8.8.4/8.8.4)
id LAA05170 for frank;Thu 25 Mar 1999 11:00:25 GMT
Date: Thu,25 Mar 1999 11:00:25 GMT
From:RHS Linux User <[historical email redacted]>
Message-Id:<[historical message identifier redacted]>
To:[historical email redacted]
Subject:test
Status:R
This is a mail test
&
mail命令还有很多其它用法,例如发送事先准备好的信件,或一次送信给若干人。还可以用其它方法送信件。
另附message的使用技巧:
当Linux系统处于多用户的情况下,有时在终端上会突然显示出下述信息:
Message from renee tty2…
并伴随出现一阵嘟嘟响声。这是用户renee想和你通话而产生的信号。若你用如下命令响应他:
$ write renee
这就建立起了你和renee的通信线路,renee在他的终端上键入的内容同时显示在你的终端上,反之你键入的内容也显示在renee的终端上。为区分终 端上哪些是你输入的,哪些是renee输入的,我们使用如下通话协议:(o)表示一段话说完,并让对方发话,(oo)代表通话结束并退出程序。
renee’s terminal: frank terminal:
[renee@xteam renee]$ write frank
$ Message from renee tty2…
$write renee
[renee@xteam renee]$Message from you tty1…
did you forget lunch? (o)
did you forgeet lunch? (o)
ten minutes (o)
ten minutes (o)
ok (oo)
ok (oo)
ctl-d
EOF
Ctl-d
EOF
[renee@xteam renee]$ $
除CTL-d键外,也可以使用DELETE退出write命令。
如果你不愿意别人干扰你的工作,可以使用mesg命令拒绝接受通话。当你向一个拒绝接收通话的用户发写命令、或者向没有注册的用户要求通话时,write命令会显示不能通话的原因。
转自:http://edu.codepub.com/2010/0413/21978.php
