grep -P不再起作用,我该如何重写我的搜索?

147

看起来新版本的OS X不再支持grep -P,这导致我的一些脚本停止工作,例如:

var1=`grep -o -P '(?<=<st:italic>).*(?=</italic>)' file.txt`

我需要将grep的结果捕获到一个变量中,并且我需要使用零宽断言,以及\K

var2=`grep -P -o '(property:)\K.*\d+(?=end)' file.txt`

期待您提供任何可行的替代方案。


8
安装GNU Grep怎么样? - Kent
6
@Kevin:它在10.8中被移除了。 - Lri
@Kent,您能详细说明一下如何做到这一点吗? - drevicko
12
OS X的grep在10.7中是grep(GNU grep) 2.5.1,但在10.8中更改为grep(BSD grep) 2.5.1-FreeBSD。我猜这是因为GPL的原因。FreeBSD的grep也是基于GNU grep,两个版本的grep都来自2002年。--label-u/--unix-byte-offsets也在10.8中被删除。在10.8中添加了-z/--decompress, -J/--bz2decompress, --exclude-dir, --include-dir, -S, -O-p-Z--null更改为--decompress - Lri
3
OS X自带的FreeBSD grep版本是2002年的,而https://wiki.freebsd.org/BSDgrep仍然表示“唯一的待办事项是提高性能”,所以是的。在我的iMac上重复运行`time grep aa /usr/share/dict/words>/dev/null`,使用OS X的grep大约需要0.09秒,而使用新的GNU grep只需要大约0.01秒。 - Lri
显示剩余5条评论
13个回答

145
如果您的脚本仅供您使用,您可以从 homebrew-core 安装 grep,使用 brew
brew install grep 
然后它可以作为 ggrep (GNU grep) 使用。它不会替换系统的 grep(你需要将安装的 grep 放在系统的 grep 之前,加入到 PATH 中)。
通过 brew 安装的版本包括 -P 选项,所以你不需要更改脚本。
如果您需要使用这些命令的普通名称,可以像下面这样从 bashrc 添加一个 "gnubin" 目录到您的 PATH 中:
PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"
你可以将这行导出到你的~/.bashrc或者~/.zshrc文件中,以保持它在新会话中仍然有效。
请参见此处,了解关于旧的--with-default-names选项及其(最近)被移除的利弊的讨论。

3
@pepper,什么没有起作用?很可能是路径设置不正确——请输出which grep的结果是什么?应该是/usr/local/bin/grep。在仔细检查问题之前就进行投票否定有点不好意思哦! - drevicko
2
可能最好将/usr/local/bin添加到您的PATH前面。我相信Brew应该设置好了?您使用了--default-names吗?无论如何,很高兴它能正常工作(: 不确定是否可以绕过它进行黑客攻击,但我认为积分系统是这个网站成为如此好的资源的原因之一。 - drevicko
1
是的,我确实使用了--default-names和brew。不确定将/usr/local/bin放在路径的前面是否比别名更好,这只是一种替代方案。 - pepper
12
一种替代 --with-default-names 的方法是将 alias grep='ggrep' 添加到您的 bash 配置文件中,并让 brew dupes 保留其前缀。 - rymo
4
--with-default-names 已经从brew中删除。我不得不使用 brew install grep 来获取 ggrep,然后像@rymo所说做 alias grep='ggrep' - Henge
显示剩余3条评论

87
如果您想尽可能地减少工作量,请更改。
grep -P 'PATTERN' file.txt

perl -nle'print if m{PATTERN}' file.txt

并改变

grep -o -P 'PATTERN' file.txt

perl -nle'print $& while m{PATTERN}g' file.txt

所以你得到:

var1=`perl -nle'print $& while m{(?<=<st:italic>).*(?=</italic>)}g' file.txt`
var2=`perl -nle'print $& while m{(property:)\K.*\d+(?=end)}g' file.txt`

在您的特定情况下,您可以通过额外的工作实现更简单的代码。

var1=`perl -nle'print for m{<st:italic>(.*)</italic>}g' file.txt`
var2=`perl -nle'print for /property:(.*\d+)end/g' file.txt`

1
这个很好用,但它返回所有匹配项,而我使用的grep只返回第一个匹配项。有什么办法只返回第一个匹配项吗? - kugyousha
1
@ironintention:在管道的末尾添加 | tail -1 - Peter
grep 总是返回所有匹配的行(除非您使用其中一个选项,它不打印任何内容)。无论如何,if (/.../) { print $1; last; } 将导致它仅打印第一个匹配项。 - ikegami
2
@Christian,如果使用适当的XML解析器(如XML :: LibXML),只需要3行即可完成它。(关键代码:say $_-> textContent for $doc->findnodes('//loc'); - ikegami
@Ikegami 我只需要针对特定用例使用一次。结果会被丢弃。我现在很满意。无论如何,感谢您让我知道libxml。有时候我会后悔自己缺乏perl-fu。 - Christian
显示剩余2条评论

13
安装ack并使用它来代替。Ack是一个用Perl编写的grep替代品,具有完整的Perl正则表达式支持。

我想查看这个,但这是工作电脑,所以我们不能安装任何东西。 - kugyousha
@ironintention:如果您可以安装Perl模块,那就没问题了。即使您无法添加到本地Perl安装中,您也可以始终使用local::lib。 - Michael Carman
ack 的设计是完全自足的,您不需要真正安装它。如果您可以保存一个文件,并将其标记为可执行文件,如果需要,更新您的 PATH,那么您就可以开始使用了。 - tripleee
请问能否提供替换上述内容的ack语法? - William Entriken
@FullDecent:它几乎是相同的:ack -o '(property:)\K.*\d+(?=end)' file.txt-o 的意思相同,但你不需要在 ack 中使用 -P)。 - Michael Carman

11

OS X倾向于提供BSD而不是GNU工具。 但是,它确实带有egrep,这可能是您执行正则表达式搜索所需的全部。

例如:egrep 'fo+b?r' foobarbaz.txt

来自OSX grep man页面的片段:

grep用于简单模式和基本正则表达式(BRE); egrep可以处理扩展正则表达式(ERE)。


9
直接调用命令“egrep”已经过时。同样的功能也可以使用命令“grep -E”实现。它是 Perl 的一个悲伤的阴影,缺乏环视断言、大部分反斜杠转义字符、选项、条件等等。高级用户会讨厌它,但至少它能够完成工作。 - Dewi Morgan
2
谢谢。我需要的正是grep -E而不是grep -P - asmaier

8

使用 Perl;

perl -ne 'print if /regex/' files ...

如果您需要更多的grep选项(我看到您至少想要-o选项),那么网络上有各种pgrep实现,其中许多是用Perl编写的。

如果“几乎Perl”足够好,PCRE附带了pcregrep。


6

还有另一种选择:pcregrep

Pcregrep 是一个带有 Perl 兼容正则表达式的 grep。它与 grep -P 完全相同。因此,它将与您的脚本兼容。

可以使用 homebrew 进行安装:

brew install pcre


Error: No available formula for pcregrep - Aaron Brager
GaborMarton,我编辑了你的回答,包括@Martin的修正意见,并不得不调整格式以实现最小更改。 - Daniel Baird

4

那么使用'-E'选项如何呢?对我来说效果很好,例如,如果我想从php -m检查php_zipphp_xmlphp_gd2扩展,我会使用:

php -m | grep -E '(zip|xml|gd2)'

1
这个可以工作。Mac使用FreeBSD grep,而Linux使用GNU grep...所以这个修复程序在我的macOS Sierra上起作用了。 - jimh

3

这个答案和被接受的答案相同,但是不需要使用-P开关,因为我可用的两台机器上都没有该开关。

find . -type f -exec perl -nle 'print $& if m{\r\n}' {} ';' -exec perl -pi -e 's/\r\n/\n/g' {} '+'

2
这个方法对我有效:

    awk  -F":" '/PATTERN/' file.txt

0

通过管道传递查找输出,使用 Perl 单行正则表达式。我使用了 回顾前瞻(获取 HTML 中的 src 链接)和 前瞻 来匹配",并将 curl 的输出(HTML)输入其中。

bash-3.2# curl stackoverflow.com | perl -0777 -ne '$a=1;while(m/(?<=src\=\")(.*)(?=\")/g){print "Match #".$a." "."$&\n";$a+=1;}'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  239k  100  239k    0     0  1911k      0 --:--:-- --:--:-- --:--:-- 1919k
Match #1 //ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
Match #2 //cdn.sstatic.net/Js/stub.en.js?v=fb6157e02696
Match #3 https://ssum-sec.casalemedia.com/usermatch?s=183712&amp;cb=https%3A%2F%2Fengine.adzerk.net%2Fudb%2F22%2Fsync%2Fi.gif%3FpartnerId%3D1%26userId%3D
Match #4 //i.stack.imgur.com/817gJ.png" height="16" width="18" alt="" class="sponsor-tag-img">elasticsearch</a> <a href="/questions/tagged/elasticsearch-2.0" class="post-tag" title="show questions tagged &#39;elasticsearch-2.0&#39;" rel="tag">elasticsearch-2.0</a> <a href="/questions/tagged/elasticsearch-dsl" class="post-tag" title="show questions tagged &#39;elasticsearch-dsl&#39;" rel="tag
Match #5 //i.stack.imgur.com/817gJ.png" height="16" width="18" alt="" class="sponsor-tag-img">elasticsearch</a> <a href="/questions/tagged/sharding" class="post-tag" title="show questions tagged &#39;sharding&#39;" rel="tag">sharding</a> <a href="/questions/tagged/master" class="post-tag" title="show questions tagged &#39;master&#39;" rel="tag
Match #6 //i.stack.imgur.com/tKsDb.png" height="16" width="18" alt="" class="sponsor-tag-img">android</a> <a href="/questions/tagged/linux" class="post-tag" title="show questions tagged &#39;linux&#39;" rel="tag">linux</a> <a href="/questions/tagged/camera" class="post-tag" title="show questions tagged &#39;camera&#39;" rel="tag
Match #7 //i.stack.imgur.com/tKsDb.png" height="16" width="18" alt="" class="sponsor-tag-img">android</a> <a href="/questions/tagged/firebase" class="post-tag" title="show questions tagged &#39;firebase&#39;" rel="tag"><img src="//i.stack.imgur.com/5d55j.png" height="16" width="18" alt="" class="sponsor-tag-img">firebase</a> <a href="/questions/tagged/firebase-authentication" class="post-tag" title="show questions tagged &#39;firebase-authentication&#39;" rel="tag
Match #8 //i.stack.imgur.com/tKsDb.png" height="16" width="18" alt="" class="sponsor-tag-img">android</a> <a href="/questions/tagged/ios" class="post-tag" title="show questions tagged &#39;ios&#39;" rel="tag">ios</a> <a href="/questions/tagged/in-app-purchase" class="post-tag" title="show questions tagged &#39;in-app-purchase&#39;" rel="tag">in-app-purchase</a> <a href="/questions/tagged/piracy-protection" class="post-tag" title="show questions tagged &#39;piracy-protection&#39;" rel="tag
Match #9 //i.stack.imgur.com/tKsDb.png" height="16" width="18" alt="" class="sponsor-tag-img">android</a> <a href="/questions/tagged/unity3d" class="post-tag" title="show questions tagged &#39;unity3d&#39;" rel="tag">unity3d</a> <a href="/questions/tagged/vr" class="post-tag" title="show questions tagged &#39;vr&#39;" rel="tag
Match #10 http://pixel.quantserve.com/pixel/p-c1rF4kxgLUzNc.gif" alt="" class="dno
bash-3.2# date
Mon Oct 24 20:57:11 EDT 2016

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接