有一种简单的方法可以将“原始”字符串传递给grep吗?

时间:2012-08-08 00:33:09

标签: bash escaping grep command-line-interface

从命令行使用时,

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-escaped版本,并且使用反引号,可以很容易地将其传递给grep调用:

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

但是,它显然不适用于此:输出中的某些字符不会被转义,而有些则不必要。例如:

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

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

是否有一个cli工具接受原始字符串并返回字符串的bash-escaped版本,可以直接用作grep 的模式?如果没有,我怎么能用纯粹的bash来实现呢?

6 个答案:

答案 0 :(得分:40)

如果您想搜索确切的字符串,

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

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

(这通常也以fgrep的形式提供。)

答案 1 :(得分:21)

如果您试图让grep使用扩展正则表达式语法,那么执行此操作的方法是使用grep -E(又名egrep)。您还应该了解grep -F(又名fgrep)以及更新版本的GNU Coreutils grep -P

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

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

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

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

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

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

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

答案 2 :(得分:19)

当我使用grep -E和用户提供的字符串时,我用这个

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

示例运行

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

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

e.g。如果你想找到以用户内容开头的每一行,用户提供有趣的字符串为。*

userdata=".*"
grep -E -- "^$(ere_quote "$userdata")" <<< ".*hello"
# if you have colors in grep you'll see only ".*" in red

答案 3 :(得分:3)

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

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

这个会:

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

答案 4 :(得分:0)

下面仅需说明示例,该示例显示grep将子字符串“ -B”解释为命令行选项,并且命令失败。

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

grep 在这种情况下有一个特殊的选择:

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

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

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

答案 5 :(得分:0)

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])/.../"(只要您不使用\[]而不是{ {1}}。
  • 例如,
  • /甚至在不直接引用的极端情况下工作
      引用
    • Leading -,以便不会被quote误解为选项。
    • 用引号引起来的空格使它们不会被grep删除。

$(...)仅在quote包含换行符时失败。但总的来说,这没有解决办法,因为[STRING]grep之类的工具可能不支持其搜索模式中的换行符(即使它们写为sed)。

还有一个缺点,就是报价输出通常比未报价输入长三倍。