Grep 查找词干并仅打印单词(而不是行)

Grep 查找词干并仅打印单词(而不是行)

我试图仅打印通过正则表达式找到的匹配单词。下面,我想要OPENSSL_NO_*源代码中存在的所有选项:

$ grep -IR OPENSSL_NO
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_CMS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_EC
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_OCSP
fuzz/asn1.c:#ifndef OPENSSL_NO_TS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_DSA
...

当我尝试通过仅打印完整单词来修剪输出时:

$ grep -oIR "OPENSSL_NO*"
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
...

当我尝试 awk 时,它会打印整行:

$ grep -IR OPENSSL_NO | awk '/OPENSSL_NO[_A-Z0-9_]/{ print $0 }'
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_CMS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_EC
...

和:

$ grep -IR OPENSSL_NO | awk '/\<OPENSSL_NO\>'
awk: line 1: runaway regular expression /\<OPENSSL_ ...

和:

$ grep -Eo -IR 'OPENSSL_NO_[A-Z0-9_]'
fuzz/asn1.c:OPENSSL_NO_R
fuzz/asn1.c:OPENSSL_NO_R
fuzz/asn1.c:OPENSSL_NO_C
fuzz/asn1.c:OPENSSL_NO_D
fuzz/asn1.c:OPENSSL_NO_E

和:

$ grep -IR OPENSSL_NO | sed -n 's/.*\(OPENSSL_NO\).*/\1/p'
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
...

如何匹配一个单词然后只打印该单词?


考虑到有很多问题,这显然是一项痛苦的任务。以下是我无法适应我的[简单?]问题的各种问题:

答案1

*正则表达式中的意思是0 个或多个前面的原子。您将它与*shell 通配符运算符混淆了,它的含义是0 个或多个字符

OPENSSL_NO_*表示OPENSSL_NO后面跟着0个或多个下划线。

你想要:

grep -o 'OPENSSL_NO_.*'

哪里.是匹配单个字符的正则表达式运算符。

或者:

grep -o 'OPENSSL_NO_[[:alnum:]]*'

0 个或多个字母数字字符(在语言环境支持的任何字母脚本中)。

扩展正则表达式(如grep -E)也有+for1 个或多个前面的原子。和基本的正则表达式(不带 -E),您可以使用\{1,\}它来代替。

有些grep实现也有\w这意味着任何字母数字字符或下划线但请注意,在某些实现的某些版本中,它仅限于此A-Za-z0-9

无论如何,请注意-o/-R不是标准选项。 POSIXly,你可能想要:

sed -n 's/.*\(OPENSSL_NO_[[:alnum:]_]\{1,\}\).*/\1/p' < file

(每行只允许出现一次;如果出现多个,则只显示最右边的一个)。

这不会打印文件名。为此,您可以使用awk

find . -name '*.[hc]' -type f -exec awk 'match($0, /OPENSSL_NO_[[:alnum:]_]+/) {
  print FILENAME": "substr($0, RSTART, RLENGTH)}' {} +

答案2

正则表达式中的运算符*意味着“零个或多个”,因此 grep 非常乐意通过使用“零”个附加字符来满足该条件。

我将以某种方式扩展正则表达式,以便 grep 被迫包含该术语的其余部分:

grep -o 'OPENSSL_NO_.*$' input

或者

grep -o 'OPENSSL_NO_.*\b' input

(在这两种情况下,我都添加了一个额外的下划线)。

答案3

我已经使用 awk 命令来实现相同的目的

for i in {1..2}; do awk -v i="$i" '$i ~/^OPENSSL/ {print $i}' example.txt; done

输出

OPENSSL_NO_RFC3779
OPENSSL_NO_RFC3779
OPENSSL_NO_CMS
OPENSSL_NO_DH
OPENSSL_NO_EC
OPENSSL_NO_RFC3779
OPENSSL_NO_OCSP
OPENSSL_NO_TS
OPENSSL_NO_DH
OPENSSL_NO_DSA

相关内容