grep语法与正则表达式不同吗?

我想从这里提取已删除软件包的名称“cat /var/log/dpkg.log | grep’remove’”

2013-09-09 15:57:34 remove activity-log-manager:i386 0.9.4-0ubuntu6.2  2013-09-09 15:57:35 remove activity-log-manager-control-center:i386 0.9.4-0ubuntu6.2  2013-09-09 15:57:38 remove alacarte:all 3.6.1-0ubuntu3  2013-09-09 15:57:41 remove deepin-software-center:all 2.1.2.1~precise~NoobsLab.com  

我想只获取包名后删除和冒号之间的包的名称。 我不是一个正则表达式专家,我做了一个正则表达式似乎做了这个工作但是当我想用grep应用它时没有任何反应。 这是正则表达式评估器中正在运行的正则表达式模式

 (?<=remove)(.*?)(?=:) 

但这不起作用:

 cat /var/log/dpkg.log | grep 'remove' | grep '(?<=remove)(.*?)(?=:)' 

我在这里想念的是什么?

正则表达式语法有一个共同的核心,但有不同的风格。 您的表达式似乎包含一些特定于perl风格的特性,特别是使用描述要匹配的模式的开始和结束的复杂外观断言 ,而grep默认为仅支持更简单的基本正则表达式(BRE)语法这些零长度匹配的集合,如line-( ^$ )和word-anchors( \>\< )。

您可以使用-P命令行开关在grep中启用perl兼容的正则表达式(PCRE)支持(但请注意,手册页当前将其描述为“experimental”)。 在您的情况下,您可能希望-o开关也只打印匹配的模式,而不是整行即ie

 cat /var/log/dpkg.log | grep 'remove' | grep -oP '(?<=remove)(.*?)(?=:)' 

请注意,如果遇到不具有:i386后缀的包,则此表达式可能会失败,因为它可能会提前读取下一个单词中的匹配冒号,例如

 echo "2013-09-07 08:31:44 remove cifs-utils 2:5.1-1ubuntu2 " | grep -oP '(?<=remove)(.*?)(?=:)' cifs-utils 2 

你可能希望看看awk,例如

 cat /var/log/dpkg.log | awk '$3 ~ /remove/ {sub(":.*", "", $4); print $4}' 

除了BRE和PCRE之外,Gnu grep还有一个名为扩展正则表达式 (ERE)的模式,由-E命令行开关指定。 该手册页指出了这一点

 In GNU grep, there is no difference in available functionality between basic and extended syntaxes. 

但是,您应该注意“可用function没有区别”并不意味着语法相同。 例如,在BRE中, +字符通常被视为文字,并且仅在转义时才变为表示“前一个正则表达式的一个或多个实例”的修饰符,即

 $ echo "123.456" | grep '[0-9]+\.[0-9]+' $ echo "123.456" | grep '[0-9]\+\.[0-9]\+' 123.456 

而对于ERE则恰恰相反

 $ echo "123.456" | grep -E '[0-9]+\.[0-9]+' 123.456 $ echo "123.456" | grep -E '[0-9]\+\.[0-9]\+' 

类似的区别适用于没有和使用-r开关调用的sed

grep手册页:

对于包含与给定PATTERN匹配的行,grep搜索命名输入FILE(或标准输入,如果没有文件被命名,或者如果单个连字符 – ( – )作为文件名)。

据我所知, grep无法编辑它匹配的行; 我会使用sed或者可能是tr 。 以下任何一项应该得到你想要的:

 cat /var/log/dpkg.log | grep 'remove' | sed 's/.*remove \([^:]*\):.*/\1/' cat /var/log/dpkg.log | grep 'remove' | sed -E 's/.*remove ([^:]*):.*/\1/' cat /var/log/dpkg.log | sed -n '/remove/s/.*remove \([^:]*:\).*/\1/p' cat /var/log/dpkg.log | sed -nE '/remove/s/.*remove ([^:]*):.*/\1/p' 

老实说,我不确定你的(?<=remove)(.*?)(?=:)正试图完成什么。 在正则表达式中,括号用于定义捕获组:您可以看到我在sed命令中使用它们 - 在那里,匹配的所有内容将被捕获组/1的内容替换,第一组将被替换定义。