60

grep从命令行使用时不能输入“原始”字符串,因为某些字符需要转义才能不被视为文字。例如:

$ grep '(hello|bye)' # WON'T MATCH 'hello'
$ grep '\(hello\|bye\)' # GOOD, BUT QUICKLY BECOMES UNREADABLE

printf用来自动转义字符串:

$ printf '%q' '(some|group)\n'
\(some\|group\)\\n

这会生成字符串的 bash 转义版本,并且使用反引号,可以轻松地将其传递给 grep 调用:

$ grep `printf '%q' '(a|b|c)'`

然而,这显然不是为了这个:输出中的某些字符没有被转义,而有些则不必要地转义。例如:

$ printf '%q' '(^#)'
\(\^#\)

传递给时^不应转义字符。grep

是否有一个 cli 工具接受原始字符串并返回可以直接用作 grep 模式的字符串的 bash 转义版本?如果不是,我怎样才能在纯 bash 中实现这一点?

4

6 回答 6

62

如果要搜索确切的字符串,

grep -F '(some|group)\n' ...

-F告诉grep按原样处理模式,不解释为正则表达式。

(这通常也可用fgrep。)

于 2012-08-08T00:41:25.163 回答
30

如果您尝试grep使用扩展正则表达式语法,则可以使用grep -E(aka egrep)。您还应该了解grep -F(aka fgrep),并且在较新版本的 GNU Coreutils 中,grep -P.

背景:原版grep有一组相当少的正则表达式运算符;这是 Ken Thompson 最初的正则表达式实现。后来开发了一个具有扩展曲目的新版本,出于兼容性原因,获得了不同的名称。在 GNUgrep中,只有一个二进制文件可以理解传统的基本 RE 语法(如果调用为grep),以及 ERE (如果调用为 )egrep。通过使用反斜杠转义来引入特殊含义,egrep可以使用 from 的一些构造。grep

随后,Perl 编程语言进一步扩展了形式主义;这种正则表达式方言似乎也是大多数新手错误地期望grep支持的。,grep -P它确实;但这尚未在所有平台上得到广泛支持。

因此,在 中grep,以下字符具有特殊含义:^$[]*.\

egrep中,下列字符也有特殊含义:()|+?{}。(用于重复的大括号不在原来的. 中egrep。)分组括号还可以使用 、 等进行反向\1引用\2

在许多版本中grep,您可以通过在特价egrep之前放置反斜杠来获得该行为。egrep还有一些特殊的序列,如\<\>.

在 Perl 中,引入了大量额外的转义,例如\w \s \d。在 Perl 5 中,正则表达式功能得到了显着扩展,具有非贪婪匹配*? +?等、非分组括号(?:...)、前瞻、后瞻等。

...话虽如此,如果您确实想在不调用任何外部进程egrep的情况下将正则表达式转换为grep正则表达式,请尝试每个特殊字符;但要认识到这不能正确处理字符类、否定字符类或反斜杠转义。${regex/pattern/substitution}egrep

于 2012-08-08T04:57:13.513 回答
30

当我将 grep -E 与用户提供的字符串一起使用时,我用这个转义它们

ere_quote() {
    sed 's/[][\.|$(){}?+*^]/\\&/g' <<< "$*"
}

示例运行

ere_quote ' \ $ [ ] ( ) { } | ^ . ? + *'
# output
# \\ \$ \[ \] \( \) \{ \} \| \^ \. \? \+ \*

这样您就可以安全地在正则表达式中插入带引号的字符串。

例如,如果您想查找以用户内容开头的每一行,用户提供有趣的字符串为 .*

userdata=".*"
grep -E -- "^$(ere_quote "$userdata")" <<< ".*hello"
# if you have colors in grep you'll see only ".*" in red
于 2013-06-06T00:26:25.623 回答
6

我认为以前的答案并不完整,因为他们错过了一件重要的事情,即以破折号(-)开头的字符串。所以虽然这不起作用

echo "A-B-C" | grep -F "-B-"

这个将:

echo "A-B-C" | grep -F -- "-B-"
于 2017-04-26T17:33:52.340 回答
1
quote() {
    sed 's/[^\^]/[&]/g;s/[\^]/\\&/g' <<< "$*"
}

用法:grep [OPTIONS] "$(quote [STRING])"

这个功能有一些实质性的好处:

  • quote独立于正则表达式的味道。您可以quote
    • grep ( -G)`(BRE,默认值)
    • grep -E(ERE)
    • grep -P(PCRE)
    • sed ( -E) "s/$(quote [STRING])/.../"(只要您不使用\,[]代替/)。
  • quote甚至可以在不直接引用相关的极端情况下工作,例如
    • 前导-被引用,以免它们被误解为选项grep
    • 尾随空格被引用,以便不被删除$(...)

quote只有在[STRING]包含换行符时才会失败。但一般来说,这个问题没有解决办法,因为像这样的工具grep并且sed可能不支持其搜索模式中的换行符(即使它们被写为\n)。

此外,还有一个缺点,即引用的输出通常比未引用的输入长三倍。

于 2020-08-19T09:18:54.447 回答
0

只想评论下面的示例,该示例显示子字符串“-B”被 grep 解释为命令行选项并且命令失败。

echo "A-B-C" | grep -F "-B-"

grep对于这种情况有一个特殊的选项:

-e PATTERNS, --regexp=PATTERNS 使用 PATTERNS 作为模式。如果此选项被多次使用或与 -f (--file) 选项结合使用,则搜索所有给定的模式。此选项可用于保护以“-”开头的模式。

因此,该问题的解决方法是:

echo "A-B-C" | grep -F -e "-B-" -
于 2020-06-06T08:20:58.427 回答