SMTP 邮件自动清理
如果您的 Linux 系统 активно处理电子邮件,尤其是 для нескольких пользователей, вы можете обнаружить, что почта занимает много места там, где она находится, обычно в одном из /var/mail (системы, совместимые с System V), /usr/mail (BSD), /usr/spool/mail или /var/spool/mail (где моя система Debian GNU/Linux хранит ее). Представленный здесь набор сценариев Bash предоставляет способ уменьшить пространство, используемое почтовыми файлами, путем очистки старых сообщений.
有几个月,我作为承包商在一家公司工作,该公司使用 Unix 系统处理其员工的所有电子邮件。数百名用户从单个主机检索他们的邮件,该主机既充当简单邮件传输协议 (SMTP) 服务器(用于常规邮件传输),又充当邮局协议 3 (POP3) 服务器(供客户端检索邮件而无需登录主机)。虽然它有一个自制的邮件清除系统,但用户可能会不小心破坏该系统,导致文件无限制地增长。
现有的邮件清除系统作为每晚的 cron 作业运行。它会确定 60 天前的日期,并构造一个 SMTP 邮件头格式的字符串。然后,它会循环遍历 /var/mail 中的所有用户邮件文件,使用 grep 命令查找该字符串并记录它出现的行。对于包含该字符串的每个文件,它将使用 tail 命令丢弃目标日期字符串所在行之前的所有内容。
如果邮件完好无损地到达,保持完整,始终按日期顺序出现,并且 cron 每天调用此作业,则此方法通常会有效。但是,有时 cron 不会运行作业,邮件并非总是按日期顺序到达,并且针对邮件文件运行的其他软件可能会重新排序邮件。由于现有解决方案的全有或全无的保留或丢弃方法,它存在以下问题
如果较旧的日期出现在较新的日期之后,则可能会丢失较新的邮件。
如果邮件正文具有与搜索字符串匹配的未转义字符串,则可能会丢失邮件的一部分。
如果较旧的邮件乱序出现,它们可能会保留比预期更长的时间,因为保留/丢弃的决定是基于简单地查找字符串,而不是检查每封邮件的日期。
它依赖于查找确切的日期字符串,而不是进行可以丢弃早于目标日期的邮件的数值比较。包含无效标头并位于邮件文件末尾的邮件可能会保留,即使它的期限已超过保留期。
进一步的影响是一些邮件阅读器可能无法优雅地处理损坏的邮件。
为了找到更可靠的解决方案,我寻求现有的免费程序来满足我的需求。我咨询了我的同行,包括 Internet 邮件列表中的同行,但没有得到有用的回应。对 Internet Unix 档案的搜索也没有发现任何东西。因此,我只能自己创建解决方案。
为了确保可以接受的邮件处理,我确定了以下要求
每封邮件都必须经过单独检查,以根据其创建日期保留或丢弃它。
需要一种可靠的方法来确定邮件的开始和结束位置。
因为我发现日期格式略有不同(邮件处理程序仅在一定程度上遵循规则),所以我需要将每封邮件的日期转换为一个简单的数字,并使用该数字来选择保留/丢弃。
我发现 formail,procmail 邮件处理包的一部分,可以一次性将 SMTP 邮件文件拆分为单独的邮件并修复损坏的标头。这种能力使能够单独检查每封邮件,以决定是否保留或丢弃它。由于每封邮件都将接受单独检查,因此日期顺序并不重要。
第一个脚本 mailrm.sh(参见列表 1)需要一个命令行参数,命名要保留的邮件天数。运行时,它会设置所需的变量并开始检查 $MAILDIR 中的每个邮件文件。它使用位于 $FORMAIL 中的 formail 来验证、修复并将每个邮件文件拆分为单独的邮件。
然后,mailage.sh 脚本(参见列表 2)检查每封邮件,以确定是否保留它。首先,它检查邮件头的“From”行以查找日期,并根据需要移动字段。(如果 formail 必须修复邮件日期,则生成的日期中没有时区。)然后,它将邮件日期与计算出的今天减去要保留邮件的天数的值进行比较。如果邮件较新,则脚本将邮件连接到 STDOUT,并将其保存到临时文件中。如果邮件较旧,则脚本退出,不输出任何内容。
之后,如果新输出文件的行数与原始文件不同,则将其移动到原始文件的位置,重置其所有权并限制其权限。如果原始邮件文件现在长度为零,则 mailrm.sh 会将其删除。如果用户已从系统中删除,留下他的邮件,则此脚本会在其中的所有邮件过期后删除他的邮件文件。
第三个脚本 maildate.sh(参见列表 3)返回“MMM DD YYYY”格式的输入日期自 1900 年以来的天数整数。返回的整数对于计算两个日期之间的差值非常有用。
将脚本复制到您喜欢的目录。我使用了 /usr/local/bin。编辑 mailrm.sh 和 mailage.sh 以反映您的邮件目录和 formail 的位置。然后只需使用要保留邮件的天数作为参数运行脚本即可。例如,要清除早于 60 天的邮件
/usr/local/bin/mailrm.sh 60
这个邮件清除解决方案是完美的吗?不,它没有锁定邮件文件,如果用户的邮件客户端频繁轮询或此作业在繁忙时段运行,这可能会造成问题。解决此问题的一些可能步骤可能包括在脚本运行时停止 sendmail,阻止 POP3 服务器运行以及收紧邮件目录的权限以防止非超级用户访问。由于它通常会在凌晨通过 cron 运行,因此发生冲突的可能性很低。
此外,此解决方案依赖于可能无法按预期运行的外部实用程序。formail 可能无法正确处理所有邮件标头,尽管我尚未遇到问题。cat 可能不喜欢邮件中可能出现的某些字符,从而导致邮件文本丢失。我使用 cat 的问题很少,但您的体验可能会有所不同。
maildate.sh 中的日期转换逻辑很简单。它对于 1900 年不准确,并且闰年计算在 2099 年之后将无法正确工作。但是,它非常适合计算两个日期之间的差异,并且速度相当快。
由于我使用三个脚本来绕过 Bash 中的参数传递限制,因此此软件包的运行速度可能比预期的要慢(因为必须重复派生进程)。将脚本重写为单个 Perl 脚本可能会有所帮助——我的 Perl 技能对于这个项目来说太有限了。
如果您需要自动邮件清除,这些脚本可以帮助您实现目标。至少,它们可能会为您自己的解决方案提供想法。如果您创建了更优雅的解决方案,我很乐意听到。
本文中引用的所有列表都可以通过匿名下载文件 ftp://ftp.linuxjournal.com/lj/listings/issue47/2118.tgz 获得。
Michael S. Keller (mskeller@paranet.com) 是 Paranet, Inc. 的技术分析师,Paranet, Inc. 是一家由 Sprint 拥有的全国性网络服务提供商。他使用计算机已有 20 年,使用 Unix 变体已有 7 年。Paranet 的虚拟主页位于 http://www.paranet.com/。