用于词干的 Grep 并仅打印单词(而不是行)

2 text-processing pattern-matching

我正在尝试仅打印通过正则表达式找到的匹配单词。下面,我想要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
...
Run Code Online (Sandbox Code Playgroud)

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

$ 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
...
Run Code Online (Sandbox Code Playgroud)

当我尝试 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
...
Run Code Online (Sandbox Code Playgroud)

和:

$ grep -IR OPENSSL_NO | awk '/\<OPENSSL_NO\>'
awk: line 1: runaway regular expression /\<OPENSSL_ ...
Run Code Online (Sandbox Code Playgroud)

和:

$ 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
Run Code Online (Sandbox Code Playgroud)

和:

$ grep -IR OPENSSL_NO | sed -n 's/.*\(OPENSSL_NO\).*/\1/p'
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
...
Run Code Online (Sandbox Code Playgroud)

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


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

Sté*_*las 6

*在正则表达式中表示0 个或多个前面的 atom。您将它与*shell 通配符运算符混淆了,它表示0 或多个字符

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

你会想要:

grep -o 'OPENSSL_NO_.*'
Run Code Online (Sandbox Code Playgroud)

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

或者:

grep -o 'OPENSSL_NO_[[:alnum:]]*'
Run Code Online (Sandbox Code Playgroud)

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

扩展的正则表达式(如grep -E)也有+for 1 个或多个前面的 atom。使用基本正则表达式(不带 -E),您可以\{1,\}改用。

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

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

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

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

那不会打印文件名。为此,您可以awk改用:

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