我有一个 bash 脚本可以定期清理邮件队列。出于某些原因,我们选择删除所有发送至 @mms.att.net 和其他 email2SMS 网关且在队列中超过 9 小时但仍未送达的电子邮件。
简单来说,该脚本执行以下操作:
domains=`cat /etc/mail/email2textdomains.txt`
egrep $domains /var/log/maillog | .... other tasks
的内容/etc/mail/email2textdomains.txt
正是
"mms.att.net|vtxt.com|vtext.com|vzwpix.com"
因此,egrep 行应该是这样的,这正是我在命令行中输入的内容。
egrep "mms.att.net|vtxt.com|vtext.com|vzwpix.com" file | ...
如果我像这样运行它,那么它是一个 5 个以上阶段的命令管道,每个命令从前一个标准输出读取标准输入。这显然不是我想做的搜索。
egrep mms.att.net|vtxt.com|vtext.com|vzwpix.com file | ...
然而,在运行时,两个双引号的处理方式不同 - 它们成为字符串的一部分,所以我们本质上是在搜索
- “mms.att.net
- vtxt.com
- vtext.com
- vzwpix.com”
显然,我误解了引用的工作原理 - 解决方案是更改包含的行以删除双引号,导致一行不应该工作,但可以。
我尝试通过管道进行测试,od -a
不显示任何非打印字符。
为什么它有效,使得内容/etc/mail/email2textdomains.txt
正是
mms.att.net|vtxt.com|vtext.com|vzwpix.com
什么时候应该像所写的那样是一个很长的失败管道?
尝试调试此类事情时,一个很棒的工具是
set -x
. 使用它,我们可以准确地看到您的命令正在做什么:如您所见,
$domains
包括引号。因此,当您将其与 一起使用时grep
,您会得到:您想要做的是在将数据传递给命令之前
grep
在 shell 级别使用引号,但由于引号是变量数据的一部分,因此它们会像任何其他字符一样被处理。最简单的解决方案是从文件中删除引号,然后仅引用变量,这无论如何都是最佳实践:顺便说一句, using
var=$(command)
比 using 更受欢迎var=`command`
,因为前者更清晰并且允许更多嵌套,并且egrep
不推荐使用grep -E
。另请注意,这
.
是一个匹配任何单个字符的正则表达式运算符,因此实际上会找到包含后跟任何单个字符、后跟任何单个字符、后跟 的grep mms.att.net
行。例如,它也会匹配包含.mms
att
net
hammstattinet.com
因此,要构建一个
E
与包含任何这些域的行相匹配的扩展正则表达式,您不仅需要删除 s,还要转义"
域名中恰好也是正则表达式运算符的所有字符。对于有效域名,应限制为.
.另请注意,对于空正则表达式,不同实现的行为有所不同
grep
,但其中许多会报告所有行,因此您可能需要对其进行特殊处理。所以:
或者,您可以将
|
s 替换为换行符,并使用(以前的)-F
选项来查找固定字符串:grep
fgrep
F
@Kaz 应该写下他的评论,以便它可以成为可接受的答案。
如果您希望避免,
eval
那么我认为您应该重写代码以添加额外的引号。我过于简单化的规则是每个美元符号都应该放在双引号内,除非你更了解。我将更改为
/etc/mail/email2textdomains.txt
每行一个域,以利用 grep 允许换行符作为表达替代项的一种方式这一事实,即并说
引号仅位于第一行以满足我的规则,不需要它们。这
--
是为了防止-
文本域文件中出现前导。使用直grep
而不是egrep
或grep -E
来增加可移植性。实际上你正在写