【问题标题】:Using grep with a negative lookahead assertion使用带有否定前瞻断言的 grep
【发布时间】:2021-05-31 21:38:38
【问题描述】:

我的问题与这篇文章完全相同,但是 bash 中的正则表达式对我不起作用。 RegExp exclusion, looking for a word not followed by another

我想包含包含单词“Tom”的 csv 文件的所有行,除非它后面跟着“Thumb”。

  • 包括:汤姆坐在海边。
  • 不包括:Tom Thumb 坐在海边。
  • 包括:汤姆和大拇指汤姆坐在海边。

当我在 regex101.com 上试用时,正则表达式 Tom(?!\s+Thumb) 有效。

但是我已经尝试了所有这些变体,但都没有奏效。我错过了什么,我该如何解决这个问题?我在 Mac 上。

cat inputfile.csv | grep Tom(?!\s+Thumb) > Tom.csv
cat inputfile.csv | egrep Tom(?!\s+Thumb) > Tom.csv
cat inputfile.csv | egrep “Tom(?!\s+Thumb)” > Tom.csv
cat inputfile.csv | grep -E Tom(?!\s+Thumb) > Tom.csv
cat inputfile.csv | grep -E “Tom(?!\s+Thumb)” > Tom.csv

【问题讨论】:

  • 请注意,grep -E 只保证 ERE 语法。 \s 是 PCRE;根据您使用的grep 的特定版本,它可能会起作用,也可能不会起作用。据我所知,?! 根本不支持 任何 ERE 实现。
  • (有些平台有一个支持 PCRE 的grep,但您需要在您的特定目标操作系统上检查man grep;通常,它是grep -P 以启用该功能)。跨度>
  • 另外,请注意grep 不是 bash 的一部分——它是一个单独的工具,由不同的团队构建,编译为不同的可执行文件。 bash 确实有自己的机制来访问标准 C 库正则表达式功能,但是当您使用 grep 时,您并没有使用 bash 的正则表达式支持——而是使用 grep 的。
  • 另外,请注意复制/粘贴到问题中的代码使用。这些不被 bash 识别为有效的引号。您必须仅使用常规双引号 -- " -- 才能将它们识别为 shell 语法。如果您使用的某些软件打开了“智能引号”,请务必在使用该程序编辑 shell 脚本之前将其关闭。
  • (请注意,仅知道您使用的是 Mac 并不能告诉我们您拥有的 grep 的版本,因为 Mac 用户可以使用 Nix、Macports 等工具安装自己的 grep 版本, 或 Homebrew -- 按我个人偏好的降序排列)

标签: regex bash grep


【解决方案1】:

POSIX ERE 无法做到这一点。

POSIX extended regular expressions 中没有否定的先行断言,这是 grep -E 激活的语法。

你能得到的最接近的方法是结合两个独立的正则表达式,一个正则匹配,一个负匹配:

grep -we 'Tom' inputfile.csv | grep -wvEe 'Tom[[:space:]]Thumb'

grep -v 排除任何匹配给定表达式的行;所以在这里,我们首先搜索Tom,然后删除Tom Thumb

但是,匹配Tom and Tom Thumb sat by the seashore 的意图使得这不可行。简而言之:你不能用标准grep 做你要求的事情,除非它有grep -P 来使你的原始语法有效。在这种情况下,您可以使用:

grep -Pwe 'Tom(?!\s+Thumb)' <inputfile.csv >Tom.csv

一个 hack 可能是临时替代

假设您有 uuidgen 可用(它似乎出现在 Big Sur 中)来生成一个临时的、不可预测的印记:

uuid=$(uuidgen)
sed -e "s/Tom Thumb/$uuid/g" <inputfile.csv \
  | grep -we 'Tom' \
  | sed -e "s/$uuid/Tom Thumb/g" >tom.csv

【讨论】:

  • 麻烦的是,这不包括我想保留的“汤姆和汤姆拇指坐在海边”。
  • 是的,我知道。查看您添加评论时正在进行的扩展。
  • 你有什么版本的grep?它有可用的grep -P 参数吗?
  • (版本号实际上不足以知道它是否有 -P 参数,因为该参数是仅在针对 libpcre 编译 GNU grep 时启用的扩展,这是可选的库依赖项;所有支持它的版本都可以使用库进行编译——因此是选项——也可以不使用它)。
  • 考虑使用我在附加到问题的评论线程中建议的包管理器之一安装 GNU grep。或者您可以在答案末尾使用 hack 两个 seds,一个在 grep 之前,一个在 grep 之后。
【解决方案2】:

Perl 解决方案怎么样:

perl -ne 'print if /Tom(?!\s+Thumb)/' inputfile.csv > Tom.csv

Perl明显支持PCRE并预装在Mac上。

  • -n 选项与sed 的选项基本相同。 它禁止自动打印。
  • -e 选项通过放置立即代码启用单行。
  • 代码print if /pattern/是打印匹配行的习惯用法,它 可以替代grep 命令。

【讨论】:

  • 感谢您的反馈。很高兴知道它有效。如果您觉得我的回答很好地解决了您的问题,如果您可以通过单击答案旁边的复选标记来接受我的回答,我将不胜感激。 BR。
【解决方案3】:

保持简单,只使用 awk,例如在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ awk '{orig=$0; gsub(/Tom Thumb/,"")} /Tom/{print orig}' file
Include: Tom sat by the seashore.
Include: Tom and Tom Thumb sat by the seashore.

【讨论】:

    【解决方案4】:

    Grep 可以使用 Perl 正则表达式 (PCRE)。来自man grep

    -P, --perl-正则表达式

    将 PATTERNS 解释为 Perl 兼容的正则表达式 (PCRE)。此选项在与 -z (--null-data) 选项结合使用时是实验性的,并且 grep -P 可能会警告未实现的功能。

    【讨论】:

    • 这是对OP问题最直接的回答;也许有一点关于跨发行版兼容性的警告。我的意思是你可以在他们后面的例子中用'P'替换'E'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 2015-10-11
    相关资源
    最近更新 更多