【问题标题】:Sending mail from a Bash shell script从 Bash shell 脚本发送邮件
【发布时间】:2010-12-21 19:12:55
【问题描述】:

我正在为 Mac 编写一个Bash shell 脚本,它通过打开一个使用 Mail.app 中的默认邮件帐户发送电子邮件的自动化应用程序来发送电子邮件通知。 automator 应用程序还附加了脚本已写入的文本文件。这个解决方案的问题是

  1. 发送时在 GUI 中可见
  2. 如果 Mail 不是当前应用程序,它会窃取焦点
  3. 这取决于 Mail.app 的帐户设置在未来是否有效

我认为要解决这些缺点,我应该直接从脚本中发送邮件,方法是直接在脚本中输入SMTP 设置、发送地址等。问题是我想在多台计算机(10.5 和 10.6)上部署此脚本,而不在计算机上启用 Postfix。是否可以在脚本中执行此操作,以便它将在 10.5 的基本 Mac OS X 安装上运行。和 10.6?

更新:我找到了 Sendmail 的 -bs 选项,这似乎是我需要的,但我不知道如何指定设置。

另外,澄清一下,我想指定 SMTP 设置的原因是通过 Postfix 发送的来自 localhost 端口 25 的邮件将被大多数公司防火墙阻止,但如果我指定服务器和备用端口,我就赢了不要遇到那个问题。

【问题讨论】:

  • 我无法提供完整的解决方案(真的不知道该怎么做),但您应该查看程序sendmail
  • 这应该在 superuser.com 或 serverfault.com 上?
  • @tommieb75:为什么?这绝对是编程相关的。

标签: macos bash email shell


【解决方案1】:

有一个程序叫做Sendmail

您可能不想使用 -bs 命令,除非您像 Martin 的示例一样将其作为原始 SMTP 发送。 -bs 用于将 SMTP 服务器作为守护进程运行。 Sendmail 将直接发送到接收邮件服务器(在端口 25 上),除非您在配置文件中覆盖它。您可以通过-C参数指定配置文件。

在配置中,你可以指定一个中继服务器(任何邮件服务器或在另一台机器上运行-bs的sendmail)

使用正确配置的中继服务器是个好主意,因为当 IT 管理邮件服务器时,他们会实施 SPFdomain keys。这样可以使您的邮件远离垃圾箱。

如果端口 25 被阻止,您有两个选择。

  1. 使用公司 SMTP 服务器。
  2. 在外部机器上运行sendmail -bd 监听的企业防火墙 在 25 以外的端口上。

相信你可以在命令行中添加配置参数。你想要的是 SMART_HOST 选项。所以像sendmail -OSMART_HOST=nameofhost.com这样调用Sendmail。

【讨论】:

  • 嗯,为什么要投反对票? sendmail -tbyron@address.com \n你好拜伦\n。对我来说很好用!
  • 因为您在回答问题上付出的努力简直不能少。
  • 如果答案足够好,我不明白为什么要付出更多的努力。更多并不总是更好。
  • 投票应该反映答案的相关性,而不是付出的努力。
  • sendmail 中的 -bs 选项可以做我想做的事情吗?
【解决方案2】:

1) 为什么不将postfix 配置为仅处理出站邮件并通过邮件网关中继?它最大的优点是它已经安装在 OS X 客户端上。

2) 安装和配置一种仅处理出站邮件的轻量级 MTA,例如 nullmailerssmtp(可通过 MacPorts 获得)。

在这两种情况下,都使用mailx(1)(或mutt,如果你想看的话)从shell脚本发送邮件。

有几个关于服务器故障的问题进入细节。

【讨论】:

  • 我想让在许多机器上部署这个脚本变得容易,而不需要配置任何其他东西,比如 postfix,所以我希望它能够独立包含在脚本中。我将研究 nullmailer 和 ssmtp。 OS X 自带的 sendmail 或 smtpd 可以做到这一点吗?
  • 错误...? sendmail 是 postfix 的一部分,而 postfix 是 os x 上的 smtpd,那么你的问题是什么?
  • 我不知道它们是postfix的组件还是可以独立运行。
  • 很高兴了解 nullmailer,但我更喜欢 postfix 而不是必须安装任何东西来简化此脚本的部署。我找不到任何有关设置 postfix 以处理我指定的邮件服务器的出站邮件的说明。我假设我无法仅在我的 shell 脚本中指定这些设置,但理想情况下这对我来说是这样的。如果这不可能,需要修改后缀的哪些配置文件?
【解决方案3】:

在保持程序自给自足的同时,您可以做到这一点的唯一方法可能是您可以从客户端直接访问 SMTP 服务器。

如果您确实可以直接访问 SMTP 服务器,则可以使用维基百科中的 SMTP example 并将其变成如下内容:

#!/bin/bash
telnet smtp.example.org 25 <<_EOF
HELO relay.example.org
MAIL FROM:<joe@example.org>
RCPT TO:<jane@example.org>
DATA
From: Joe <joe@example.org>
To: Jane <jane@example.org>
Subject: Hello

Hello, world!
.
QUIT
_EOF

为了处理错误,我会将输出从 telnet 重定向到一个文件,然后用 grep 将其用于稍后的“成功消息”。我不确定消息应该是什么格式,但我在 SMTP 服务器的输出中看到类似“250 2.0.0 Ok: queued as D86A226C574”的内容。这将使我 grep 为“^250.*queued as”。

【讨论】:

  • -1 这是一个很好的例子,但在实际环境中毫无用处。一:错误处理在哪里?
  • 数据部分的标题和内容之间不应该有一个空行吗?
  • @Tomas:StackOverflow 中的答案大多不适用于行业强度稳健性。作为概念证明,这真的很好。
  • 从技术上讲,telnet 是一个外部程序,因此这不是自给自足的 :) 但是,对于 SMTP 示例,+1。
  • @Kimvais: grep、bash 等都不是... ;-) 另外,我没有发现 telnet 不是默认安装的一部分。
【解决方案4】:

实际上,“邮件”也可以。

mail -s "subject line" name@address.ext < filename

只要您在机器上设置了 SMTP,就可以正常工作。我认为大多数 Mac 默认情况下都会这样做。

如果您没有 SMTP,那么您唯一能做的就是通过 Mail.app。通过 mail.app 的另一种方法是通过AppleScript。当您告诉 Mail.app 通过 AppleScript 发送邮件时,您可以告诉它不要弹出任何窗口...(这仍然需要配置 Mail.app)。

Introduction to Scripting Mail 很好地描述了如何在 AppleScript 中处理邮件。

【讨论】:

  • 我目前正在使用 postfix,我的脚本包含 cat /pathto/file.txt |邮件-s“主题”地址@example.com。我意识到我可以编辑 Postfix 以使用 macosxhints.com/article.php?story=20081217161612647 指定 SMTP 服务器,但我有兴趣将设置放入 bash 脚本本身。 applescript 的想法很好。我喜欢 Mail.app 不会从用户那里窃取焦点(就像我的自动化工作流程一样),但它仍然需要正确设置 Mail.app。如果我无法将这些设置添加到我的脚本中,applescript 可能是最好的选择。
  • 您要使用哪些 smtp 设置?您可能可以设置它们,可能使用 shell 变量...
  • 哪个服务器都没有关系。 Gmail、fastmail、Kerio、Exchange 等。我想任何解决方案都适用于其中任何一个,但如果有一个解决方案只与一个提供商合作,那仍然会有帮助。正如我在上述问题的更新中提到的那样,指定备用端口也会有所帮助。
  • 请注意,您按 Control-D 发送。
  • 这是最简单的方法。它只是工作。比编写 python 脚本要好得多;-)
【解决方案5】:

这是一个简单的Ruby 脚本来执行此操作。 Ruby 在您提到的 Mac OS X 版本上提供。

替换所有标记为“替换”的位。如果失败,它会返回一个非零退出代码和一个 Ruby 回溯。

require 'net/smtp'

SMTPHOST = 'replace.yoursmtpserver.example.com'
FROM = '"Your Email" <youremail@replace.example.com>'

def send(to, subject, message)
  body = <<EOF
From: #{FROM}
To: #{to}
Subject: #{subject}

#{message}
EOF
  Net::SMTP.start(SMTPHOST) do |smtp|
    smtp.send_message body, FROM, to
  end
end

send('someemail@replace.example.com', 'testing', 'This is a message!')

您可以像这样将其嵌入到 Bash 脚本中:

ruby << EOF
 ... script here ...
EOF

有关发送 Ruby 电子邮件的其他方式,请参阅 Stack Overflow 问题How do I send mail from a Ruby program?

您也可以使用 Mac OS X 附带的其他语言:

【讨论】:

    【解决方案6】:

    这是我在各种 UNIX 系统上使用的修改后的 shell 脚本片段... (echo "${MESSAGE}" | ${uuencode} ${ATTACHMENT}$basename ${附件}) | ${mailx} -s "${SUBJECT}" "${TO_LIST}"

    uuencode 和 mailx 设置为可执行文件。其他变量来自使用 getopts 解析的用户输入。

    这确实有效,但我不得不承认我经常使用一个简单的 Java 程序来发送控制台电子邮件。

    【讨论】:

    • 不幸的是,uuencode 和 mailx 不是 OSX 的一部分。邮件是,虽然。我不清楚需要多少设置才能使其正常工作。默认情况下,发件人地址是“systemname.local”,它不提供任何信息。 :-(
    • 我不担心 systemname.local 不提供信息,因为我在主题中有识别信息,但我担心默认的后缀配置会被防火墙阻止。
    • uuenecode 和 mailx 在我们工作中的标准 solaris 安装中。我认为它们在 FreeBSD 中是标准的,但我可能是从端口中获取的,我不记得了。您可以研究 perl/python/ruby 替代方案。
    【解决方案7】:

    由于 Mac OS X 包含 Python,因此请考虑使用 Python 脚本而不是 Bash 脚本。发送部分我没有测试过,但它遵循standard example

    Python 脚本

    # Settings
    
    SMTP_SERVER = 'mail.myisp.com'
    SMTP_PORT = 25
    SMTP_USERNAME = 'myusername'
    SMTP_PASSWORD = '$uper$ecret'
    SMTP_FROM = 'sender@example.com'
    SMTP_TO = 'recipient@example.com'
    
    TEXT_FILENAME = '/script/output/my_attachment.txt'
    MESSAGE = """This is the message
    to be sent to the client.
    """
    
    # Now construct the message
    import smtplib, email
    from email import encoders
    import os
    
    msg = email.MIMEMultipart.MIMEMultipart()
    body = email.MIMEText.MIMEText(MESSAGE)
    attachment = email.MIMEBase.MIMEBase('text', 'plain')
    attachment.set_payload(open(TEXT_FILENAME).read())
    attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(TEXT_FILENAME))
    encoders.encode_base64(attachment)
    msg.attach(body)
    msg.attach(attachment)
    msg.add_header('From', SMTP_FROM)
    msg.add_header('To', SMTP_TO)
    
    # Now send the message
    mailer = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
    # EDIT: mailer is already connected
    # mailer.connect()
    mailer.login(SMTP_USERNAME, SMTP_PASSWORD)
    mailer.sendmail(SMTP_FROM, [SMTP_TO], msg.as_string())
    mailer.close()
    

    我希望这会有所帮助。

    【讨论】:

    • 该死!我想在前一刻提出同样的建议。无论如何 +1。
    • 这是个好主意。我刚刚开始学习 python,所以一旦我了解了这门语言的方法,这可能就是我想要的。
    • 我刚刚对此进行了测试,它对我帮助很大。起初它不起作用,为了让它到 wotk,我不得不(不太明白为什么)从这一行中删除参数 mailer = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) 并将它们传递给“连接”调用。任何人都可以确认/解释吗? (Python 2.7.2,OS x 10.8)
    • @KillerX 感谢您指出这一点。用法不正确。在阅读docs 时,我现在看到如果将主机/端口传递给 SMTP 构造函数,则会自动调用connect。第二次调用 connect 只是尝试重新连接到 'localhost:25',这可能是不正确的。我已经更新了示例以省略连接调用(但将其留作后人的评论)。您仅向连接方法提供主机/端口的方法也是有效的。感谢您的反馈。
    • @BSUK:在add_header('To', ...)msg.add_header('Subject', SUBJECT) 之后,其中SUBJECT 是包含主题字符串的字符串文字或变量。
    【解决方案8】:

    sendEmail 是一个脚本,可用于使用更复杂的设置从命令行发送电子邮件,包括连接到远程 smtp 服务器: http://caspian.dotconf.net/menu/Software/SendEmail/

    在 OSX 上,它可以通过 macports 轻松安装: http://sendemail.darwinports.com/

    以下是该命令的帮助页面,注意 -s、-xu、-xp 标志:

    Synopsis:  sendEmail -f ADDRESS [options]
    
    Required:
      -f ADDRESS                from (sender) email address
      * At least one recipient required via -t, -cc, or -bcc
      * Message body required via -m, STDIN, or -o message-file=FILE
    
    Common:
      -t ADDRESS [ADDR ...]     to email address(es)
      -u SUBJECT                message subject
      -m MESSAGE                message body
      -s SERVER[:PORT]          smtp mail relay, default is localhost:25
    
    Optional:
      -a   FILE [FILE ...]      file attachment(s)
      -cc  ADDRESS [ADDR ...]   cc  email address(es)
      -bcc ADDRESS [ADDR ...]   bcc email address(es)
    
    Paranormal:
      -xu USERNAME              authentication user (for SMTP authentication)
      -xp PASSWORD              authentication password (for SMTP authentication)
      -l  LOGFILE               log to the specified file
      -v                        verbosity, use multiple times for greater effect
      -q                        be quiet (no stdout output)
      -o NAME=VALUE             see extended help topic "misc" for details
    
    Help:
      --help TOPIC              The following extended help topics are available:
          addressing            explain addressing and related options
          message               explain message body input and related options
          misc                  explain -xu, -xp, and others
          networking            explain -s, etc
          output                explain logging and other output options
    

    【讨论】:

      【解决方案9】:

      sendmail 甚至 postfix 可能太大而无法安装,如果您只想从脚本发送几封电子邮件。

      例如,如果您有 Gmail 帐户,则可以使用 Google 的服务器使用 SMTP 发送电子邮件。如果您不想使用 gGoogle 的服务器,只要您可以访问某些 SMTP 服务器,它应该可以工作。

      msmtp 是一个非常轻量级的程序,可以很容易地做到这一点。他们的documentation 中有examples of configuration files

      最简单的方法是设置系统范围的默认值:

      account default
      host smtp.gmail.com
      from john.doe@gmail.com
      user john.doe@gmail.com
      password XXX
      port 587
      

      msmtp 应该很容易安装。事实上,有a port,所以它可以像port install msmtp一样简单。

      安装和配置msmtp后,您可以使用以下方式向john.doe@gmail.com发送电子邮件:

      mail -s <subject> john.doe@gmail.com <<EOF
      <mail text, as many lines as you want. Shell variables will be expanded>.
      EOF
      

      您可以将上述内容放入脚本中。详情请见man mail

      【讨论】:

        【解决方案10】:

        我为挑战准备了这个。如果您删除对“dig”的调用以获取邮件中继,则它是 100% 原生 Bash 脚本。

        #!/bin/bash
        MAIL_FROM="sfinktah@bash.spamtrak.org"
        RCPT_TO="sfinktah@bash.spamtrak.org"
        MESSAGE=message.txt
        SMTP_PORT=25
        SMTP_DOMAIN=${RCPT_TO##*@}
        
        index=1
        while read PRIORITY RELAY
        do
            RELAY[$index]=$RELAY
            ((index++))
        done < <( dig +short MX $SMTP_DOMAIN )
        
        RELAY_COUNT=${#RELAY[@]}
        SMTP_COMMANDS=( "HELO $HOSTNAME" "MAIL FROM: <$MAIL_FROM>" "RCPT TO: <$RCPT_TO>" "DATA" "." "QUIT" )
        SMTP_REPLY=([25]=OK [50]=FAIL [51]=FAIL [52]=FAIL [53]=FAIL [54]=FAIL [55]=FAIL [45]=WAIT [35]=DATA [22]=SENT)
        
        for (( i = 1 ; i < RELAY_COUNT ; i++ ))
        do
            SMTP_HOST="${RELAY[$i]}"
            echo "Trying relay [$i]: $SMTP_HOST..."
            exec 5<>/dev/tcp/$SMTP_HOST/$SMTP_PORT
            read HELO <&5
            echo GOT: $HELO
            for COMMAND_ORDER in 0 1 2 3 4 5 6 7
            do
                    OUT=${SMTP_COMMANDS[COMMAND_ORDER]}
                    echo SENDING: $OUT
                    echo -e "$OUT\r" >&5
        
                    read -r REPLY <&5
                    echo REPLY: $REPLY
                    # CODE=($REPLY)
                    CODE=${REPLY:0:2}
                    ACTION=${SMTP_REPLY[CODE]}
                    case $ACTION in
                            WAIT )          echo Temporarily Fail
                                                            break
                                                            ;;
                            FAIL )          echo Failed
                                                            break
                                                            ;;
                            OK )                            ;;
                            SENT )          exit 0
                                                            ;;
                            DATA )          echo Sending Message: $MESSAGE
                                                            cat $MESSAGE >&5
                                                            echo -e "\r" >&5
                                                            ;;
                            * )         echo Unknown SMTP code $CODE
                                                            exit 2
                    esac
            done
        done
        

        【讨论】:

        • 我认为这是一个非常酷的脚本,并为我自己的目的创建了一个稍微修改过的版本,它曾一度有效,但几年后,当我尝试它时,它失败了。不确定 bash 是否发生了变化或发生了什么变化。 Bash 抱怨说,“WAIT”案例中的“break”仅在“for”、“while”或“until”循环中有意义。实际上,“break”位于“do”循环内的“case”部分。我 90% 确定这曾经可以正常工作。
        【解决方案11】:

        用一行从 Bash 发送邮件:

        echo "your mail body" | mail -s "your subject" yourmail@yourdomain.com -a "From: sender@senderdomain.com"
        

        【讨论】:

        • 在 OS X Mavericks 上获得 send-mail: illegal option -- a
        • echo "your mail body" | mail -s "your subject" -F recipient@domain.com 为我工作
        【解决方案12】:

        试试 mtcmail。它是一个相当完整的电子邮件发件人,完全独立。

        【讨论】:

          猜你喜欢
          • 2011-07-06
          • 2018-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多