Grep 表达式过滤掉 [alnum][punct][alnum] 形式的行

Grep expression filter out lines of the form [alnum][punct][alnum]

大家好,我的第一个 post 是为了我认为很简单的事情......

我没能找到类似的例子 problem/solution。

我有数千个文本文件,其中包含数千行内容,格式为

<word><space><word><space><number>

示例:

    example for 1
    useful when 1
    for. However 1
    ,boy wonder 1
    ,hary-horse wondered 2

在上面的示例中,我想排除第 3 行,因为它包含内部标点符号

我正在尝试使用 GNU grep 2.25,但运气不好

我最初的尝试是(但是这不允许模式内部的“-”):

grep -v [:alnum:]*[:punct:]*[:alnum:]* filename 

所以尝试了这个

grep -v [:alnum:]*[:space:]*[!]*["]*[#]*[$]*[%]*[&]*[']*[(]*[)]*[*]*[+]*[,]*[.]*[/]*[:]*[;]*[<]*[=]*[>]*[?]*[@]*[[]*[\]*[]]*[^]*[_]*[`]*[{]*[|]*[}]*[~]*[.]*[:space:]*[:alnum:]* filename 

但是我需要考虑空格和 - 因为这些在字符串内部是可以接受的。

我一直在尝试使用 :punct" 集,但现在看到它包含 - 很明显那是行不通的

我目前在 TSQL 中有一个存储过程来处理这些,但是如果可能的话我更愿意在加载之前进行预处理,因为例程每个文件需要几秒钟。

有人能做到类似的事情吗?

您的正则表达式包含一长串有序的可选元素,但这意味着如果出现乱序,它将失败。例如,

[!]*[?]*

将捕获 !? 但不会捕获 ?! (当然,包含单个字符的字符 class 只等同于该单个字符,因此您不妨说 !*?*).

您可以改用单个字符 class,其中包含您要捕获的所有符号。一旦您在字母数字字符旁边看到一个,您就完成了,因此您不需要正则表达式匹配整个输入行。

grep -v '[[:alnum:]][][!"#$%&'"'"'()*+,./:;<=>?@\^_`{|}~]' filename

还要注意表达式需要如何用单引号引起来,以便 shell 不会干扰此处的许多元字符。为了使单引号字符串包含文字单引号,我暂时分成双引号字符串;参见 here 的解释(我称之为 "seesaw quoting")。

在一个字符class中,如果class需要包含],则需要在枚举列表的开头;为了对称和成语,我也把[移到了旁边

此外,正如 Jonathan Leffler 所指出的,POSIX 字符 class 名称需要位于字符 class 内;所以要匹配属于 [:alnum:] 命名集的一个字符,你说 [[:alnum:]]。 (这意味着您可以组合集合,因此 [-[:alnum:].] 涵盖字母数字加上破折号和句点。)

如果您需要将其限制为仅匹配第一个字段,请将 [[:alnum:]] 更改为 ^[[:alnum:]]\+

没有意识到 a*b*c* 匹配 任何东西 是一个常见的新手错误。您希望避免编写所有元素都是可选的表达式,因为它会匹配所有可能的字符串。专注于您想要匹配的内容(在您的情况下是一长串标点符号),然后如果确实需要,可以在其周围添加可选的上下文;但是您需要的这些越少,它就会越快 运行,也就越容易看到它的作用。作为一个快速的经验法则,a*bc* 实际上精确地等同于 b——前导或尾随的可选表达式也可以不指定,因为它们不会影响将要匹配的内容。

从表面上看,您正在寻找 'word space word space number' 架构,假设 'word' 是 'one alphanumeric optionally followed by zero or one occurrences of zero or more alphanumeric or punctuation characters and ending with an alphanumeric',并且 'space' 是 'one or more spaces' 并且'number' 是 'one or more digits'.

根据 grep -E(又名 egrep):

grep -E '[[:alnum:]]([[:alnum:][:punct:]]*[[:alnum:]])?[[:space:]]+[[:alnum:]]([[:alnum:][:punct:]]*[[:alnum:]])?[[:space:]]+[[:digit:]]+'

包含:

[[:alnum:]]([[:alnum:][:punct:]]*[[:alnum:]])?

检测带有任何标点符号并被字母数字包围的单词,并且:

[[:space:]]+
[[:digit:]]+

查找一个或多个空格或数字。

使用稍微扩展的数据文件,这会产生:

$ cat data
example for 1
useful when 1
for. However 1
,boy wonder 1
,hary-horse wondered 2
O'Reilly Books 23
Coelecanths, Dodos Etc 19
$ grep -E '[[:alnum:]]([[:alnum:][:punct:]]*[[:alnum:]])?[[:space:]]+[[:alnum:]]([[:alnum:][:punct:]]*[[:alnum:]])?[[:space:]]+[[:digit:]]+' data
example for 1
useful when 1
,boy wonder 1
,hary-horse wondered 2
O'Reilly Books 23
Coelecanths, Dodos Etc 19
$

它根据需要删除了 for. However 1 行。