将 pcregrep 翻译成 Perl 一行代码

translate pcregrep into Perl one-liner

我需要在新的 macOS 上找到所有活动的网络接口。这意味着以下带有 pcregrep 的单行代码将不起作用:

ifconfig | pcregrep -M -o '^[^\t:]+(?=:([^\n]|\n\t)*status: active)'

因为 pcregrep 在 macOS 上没有默认安装。

我试图将其翻译成 egrep 但无济于事,因为不可能进行正面的前瞻,对吗?

所以我尝试在 perl 中使用单行代码。但是下面的命令不起作用,因为开关 -pe 没有同时吞噬所有行。我也试过 -p0e

ifconfig | perl -pe 'while (<>) {if (/^[^\t:]+(?=:([^\n]|\n\t)*status: active)/){print "";};}'

如果我在同一行中使用正面前瞻搜索,它就可以工作;例如:

ifconfig | perl -pe 'while (<>) {if (/^([^\t:]+)(?=:([^\n]|\n\t)*mtu 1380)/){print "";};}'
utun0

ifconfig的典型输出:

en10: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=6467<RXCSUM,TXCSUM,VLAN_MTU,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
    ether 00:e0:4c:68:01:20
    inet6 fe80::1470:31b9:a01c:6f5e%en10 prefixlen 64 secured scopeid 0xd
    inet 192.168.178.39 netmask 0xffffff00 broadcast 192.168.178.255
    inet6 2003:ee:4f1a:ce00:864:f90c:9a11:6ad9 prefixlen 64 autoconf secured
    inet6 2003:ee:4f1a:ce00:d89a:7e34:6dd4:1370 prefixlen 64 autoconf temporary
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (1000baseT <full-duplex>)
    status: active

预期结果为:

en10

我在 macOS Monterey、zsh 和 perl 5.34

感谢您的帮助

马雷克

perl 的 -n 和 -p command-line 开关在 -e 代码周围添加一个隐式的 while (<>) {...} 块,另外 -p 在每个代码的末尾打印行迭代。因此,您需要将 -p 更改为 -n 并仅打印出匹配的行;并删除多余的和不需要的 while 循环。所以像

ifconfig | perl -ne 'print if /...../'

你可以使用

perl -0777 -nE 'say "$&" while /^[^\n\r\t:]+(?=:(?:.*\R\t)*status:\h+active)/gm'

参见regex test

这里,-0777 slurps 文件,以便正则表达式可以匹配多行文本跨度(M 等效项将换行符暴露给 pcregrep[=36= 中的模式]), say "$&" 打印所有匹配的子字符串(o 等效项,另请参阅 g 标志)。

我编辑了 [^\t:]+ 以匹配制表符、冒号和 CR/LF 字符以外的任何一个或多个字符。此外,我将 ([^\n]|\n\t)* 替换为更高效的 (?:.*\R\t)*,它匹配除换行字符以外的任何零个或多个字符的零次或多次出现,直到行尾 (.*),然后一个换行符序列 (\R),然后是一个制表符 (\t).

此外,请注意 m 标志,使 ^ 锚点也匹配任何行的起始位置。

由于ifconfig正常输出每个界面多行文本块,全部用空行分隔,方便分段阅读( -00)。那么剩下的就简单很多

ifconfig -a | perl -00 -nE'say  if /^(.+?)\s*:.*?status:\s+active/s'

我们仍然需要 /s 修饰符,使 . 也匹配换行符,因为每个段落本身都是多行字符串,模式需要跨多行匹配。


除了它不在用于此问题的 MacOS 上——没有空行分隔接口块。那么寻找段落是没有意义的(在换行符上换行不是)并且这个答案不适用于该系统。

这是一个经典的 line-by-line 方法——在该接口输出的第一行设置接口名称(行首没有空格),然后测试活动状态

perl -wnE'$ifn=, next if /^(\S[^:]+?)\s*:/; say $ifn if /status:\s+active/' file

这允许在接口名称中使用空格,这是不太可能的(甚至可能不允许)。对于不允许在名称中使用空格的更严格的模式,请使用 /^(\S+?)\s*:/(或更有效的 /^([^:\s]+)/)。 \s* 和前面的 ? 只是为了让它不捕获尾随空格(就在 : 之前),如果可能的话。

这也适用于界面块之间有空行的情况。