AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 753769
Accepted
Criggie
Criggie
Asked: 2023-08-11 12:02:04 +0800 CST2023-08-11 12:02:04 +0800 CST 2023-08-11 12:02:04 +0800 CST

Bash 将变量内的引号读取为文本,而不是引号?Bash 中有“隐式引用”吗?

  • 772

我有一个 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

什么时候应该像所写的那样是一个很长的失败管道?

bash
  • 2 2 个回答
  • 522 Views

2 个回答

  • Voted
  1. Best Answer
    terdon
    2023-08-11T18:59:58+08:002023-08-11T18:59:58+08:00

    尝试调试此类事情时,一个很棒的工具是set -x. 使用它,我们可以准确地看到您的命令正在做什么:

    $ set -x
    $ domains=$(cat domains.txt)
    ++ cat domains.txt
    + domains='"mms.att.net|vtxt.com|vtext.com|vzwpix.com"'
    

    如您所见,$domains包括引号。因此,当您将其与 一起使用时grep,您会得到:

    $ grep -E -- "$domains" file
    + grep --color -E -- '"mms.att.net|vtxt.com|vtext.com|vzwpix.com"' file
    

    您想要做的是在将数据传递给命令之前grep在 shell 级别使用引号,但由于引号是变量数据的一部分,因此它们会像任何其他字符一样被处理。最简单的解决方案是从文件中删除引号,然后仅引用变量,这无论如何都是最佳实践:

    domains=$(tr -d \" < domains.txt) &&
    grep -E -- "$domains" file
    

    顺便说一句, usingvar=$(command)比 using 更受欢迎var=`command`,因为前者更清晰并且允许更多嵌套,并且egrep不推荐使用grep -E。

    另请注意,这.是一个匹配任何单个字符的正则表达式运算符,因此实际上会找到包含后跟任何单个字符、后跟任何单个字符、后跟 的grep mms.att.net行。例如,它也会匹配包含.mmsattnethammstattinet.com

    因此,要构建一个E与包含任何这些域的行相匹配的扩展正则表达式,您不仅需要删除 s,还要转义"域名中恰好也是正则表达式运算符的所有字符。对于有效域名,应限制为..

    另请注意,对于空正则表达式,不同实现的行为有所不同grep,但其中许多会报告所有行,因此您可能需要对其进行特殊处理。

    所以:

    regex=$(
      sed 's/"//g; # remove all "s like with tr
           s/\./\\./g; # substitute .s with \.s
          ' domains.txt
    ) && 
      [ -n "$regex" ] && # check it's not empty 
      grep -E -- "$regex" file
    

    或者,您可以将|s 替换为换行符,并使用(以前的)-F选项来查找固定字符串:grepfgrepF

    domains=$(<domains.txt tr -d '"' | tr '|' '\n') &&
      [ -n "$domains" ] &&
      grep -F -- "$domains" file
    
    • 7
  2. icarus
    2023-08-11T15:04:53+08:002023-08-11T15:04:53+08:00

    @Kaz 应该写下他的评论,以便它可以成为可接受的答案。

    如果您希望避免,eval那么我认为您应该重写代码以添加额外的引号。我过于简单化的规则是每个美元符号都应该放在双引号内,除非你更了解。

    我将更改为/etc/mail/email2textdomains.txt每行一个域,以利用 grep 允许换行符作为表达替代项的一种方式这一事实,即

    mms.att.net
    vtxt.com
    vtext.com
    vzwpix.com
    

    并说

    domains="$(cat /etc/mail/email2textdomains.txt)"
    grep -- "$domains" /var/log/maillog | .... other tasks
    

    引号仅位于第一行以满足我的规则,不需要它们。这--是为了防止-文本域文件中出现前导。使用直grep而不是egrep或grep -E来增加可移植性。实际上你正在写

    grep -- "mms.att.net
    vtxt.com
    vtext.com
    vzwpix.com" /var/log/maillog | .... other tasks
    
    • 4

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve