基本正则表达式GNU扩展的平台支持

Lobsters Hottest 新闻

摘要

一篇探讨平台对基本正则表达式(BRE)GNU扩展支持的文章,具体是`\+`操作符,发现其在FreeBSD、macOS以及基于musl的发行版(如Chimera Linux)上均可正常工作。

<p><a href="https://lobste.rs/s/edml2s/platform_support_for_gnu_extensions">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/30 09:35

# 基本正则表达式中 GNU 扩展的平台支持 来源:https://www.wezm.net/v2/posts/2026/bre/ 最近我审阅了一位同事编写的 shell 脚本: `` if grep -e '@[^@]\+@' "$DIR/install.sh" ; then `` 当时我以为`\+`前面的`\`是个笔误,并指出如果要使用`\+`,可能需要传递`-E`以启用扩展正则表达式(ERE)支持。同事回复说,在基本正则表达式(BRE)中,`\\+`等同于 ERE 中的`\+`(表示一次或多次重复)。 这对我来说是个新知识!我想了解更多,于是查阅了 FreeBSD 的 `re_format(7)` 手册页(https://man.freebsd.org/cgi/man.cgi?query=re_format&apropos=0&sektion=7&manpath=FreeBSD+15.1-STABLE&format=html)。通常我对 BRE 和 ERE 区别的了解主要源于此,但其中并未提及这一点。我启动了一个 FreeBSD 虚拟机进行了简单测试,结果证实 `\\+` 确实可用。 与此同时,同事回复说,`\+` 并非 POSIX 规范中 BRE 的一部分,而是一个 GNU 扩展。引用最新的 POSIX 规范(https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html#tag_09_03_02)中的描述: > ……`\?`、`\+` 和 `\|` 是匹配字面字符 `?`、`+` 或 `|`,还是按照 ERE 特殊字符 `?`、`+` 和 `\|` 的方式行为,由实现定义。 并附有说明: > 本标准未来版本可能要求 `\?`、`\+` 和 `\|` 按照 ERE 特殊字符 `?`、`+` 和 `\|` 的方式行为。 因此,目前将 `\+` 视为 `+` 尚未标准化。 我好奇为什么 FreeBSD 的 `grep` 支持它,但 `re_format(7)` 却未提及,于是深入 FreeBSD 的源代码。这让我找到了 `regcomp.c`(https://github.com/freebsd/freebsd-src/blob/961f4814286820f242d8d5407b9fd7238e896936/lib/libc/regex/regcomp.c#L969): `` #ifdef LIBREGEX } else if (p->gnuext && EATTWO('\\', '?')) { INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); } else if (p->gnuext && EATTWO('\\', '+')) { INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); #endif `` 该功能于 2020 年 8 月引入(https://github.com/freebsd/freebsd-src/commit/18a1e2e9b9f109a78c5a9274e4cfb4777801b4fb)。`gnuext` 标志默认设置,除非在正则表达式上设置了 `REG_POSIX` 标志,而 `grep` 在基本模式下并未设置该标志(https://github.com/freebsd/freebsd-src/blob/9e1bbfb88e986b209709ea765189a3ebb6581bcd/usr.bin/grep/grep.c#L650)。 接下来我转向扩展的源头:glibc。glibc 中正则表达式语法非常可定制。基本正则表达式的定义(https://sourceware.org/git/?p=glibc.git;a=blob;f=posix/regex.h;h=2d8392cf84aeb42f9bbcd672f6e607d7be02f220;hb=bcbd6736005876dadd3d4751cad509568bc4bf28),`RE_SYNTAX_POSIX_BASIC` 如下: `` # define RE_SYNTAX_POSIX_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) `` 而 `RE_BK_PLUS_QM` 是: `` /* 如果此位未设置,则 + 和 ? 是运算符,而 \+ 和 \? 是字面量。 如果已设置,则 \+ 和 \? 是运算符,而 + 和 ? 是字面量。 */ # define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) `` 深入探究此 GNU 扩展的起源,我发现它自 1995 年起就已存在于 glibc 中(https://sourceware.org/git/?p=glibc.git;a=blob;f=posix/regex.h;h=42afd848817959e62a160404815dc235f7941700;hb=2b83a2a4d978012cdf78b648337c31091e20526d)。我想知道该扩展的支持范围有多广。以下是我的发现: - ✅ Chimera Linux(https://chimera-linux.org/)(及其他基于 musl 的发行版)—— 2016 年加入 musl libc(https://git.musl-libc.org/cgit/musl/commit/?id=25160f1c08235cf5b6a9617c5640380618a0f6ff)。 - ✅ macOS(https://en.wikipedia.org/wiki/MacOS)—— 似乎使用了大约 2009 年的 TRE 版本(https://github.com/laurikari/tre)。在 2021 年 10 月的代码转储(https://github.com/apple-oss-distributions/Libc/commit/0432a948ba54a0d62e6d3dd0b96a0f1e7dfd5fac)中似乎增加了对 `\+` 的支持(https://github.com/apple-oss-distributions/Libc/blob/71bbe350ab79eef58113991d817ccc6165061a64/regex/TRE/lib/tre-parse.c#L1635)。但对应代码在上游 TRE(https://github.com/laurikari/tre/blob/71bfcaf0af3994384987c6c2679ed7d078ffe189/lib/tre-parse.c#L1174)中似乎并不存在。 - ✅ NetBSD(https://netbsd.org/)—— 在 2021 年 2 月通过与 FreeBSD 同步(https://github.com/NetBSD/src/commit/1ee269c3a208a14da224b6e9917e2e9798961fff)支持此功能。 - ❌ OpenBSD(https://www.openbsd.org/)—— 似乎不支持(https://github.com/openbsd/src/blob/3a7ae229256d4c0f22c83dba5625ff455b6689a3/lib/libc/regex/regcomp.c#L465)。 - ❌ Illumos(https://illumos.org/)—— 似乎不支持(https://github.com/illumos/illumos-gate/blob/edd2f3461fcd719ff41d34b395ef3f5b5994fad1/usr/src/lib/libc/port/regex/regcomp.c#L700)。 - ✅ Redox OS(https://www.redox-os.org/)—— 使用 `posix-regex`(https://gitlab.redox-os.org/redox-os/posix-regex)crate,该 crate 确实实现了该扩展(https://gitlab.redox-os.org/redox-os/posix-regex/-/blob/063882aa6051b5075062d20d00b41ddc0fb8cd89/src/compile.rs#L214)。自 2018 年起(https://gitlab.redox-os.org/redox-os/posix-regex/-/commit/7648b78f45122826ca827ecd15111c4639aa68bd#line_16f7fc429_A254)。 - ✅ Haiku(https://www.haiku-os.org/)—— 支持(https://codeberg.org/haiku/haiku/src/commit/2b75ca9e1c15526b9ff7613363bb792a28a7f7ac/src/build/libgnuregex/regex.h#L179)。自 2014 年起通过导入 gnuregex(https://codeberg.org/haiku/haiku/commit/b55c918f579fb523946747cf26dde829fe7fe8c2)支持。 - ❌ SerenityOS(https://serenityos.org/)—— 不支持(https://github.com/SerenityOS/serenity/blob/341274075a32952223a8c8fe08e702fb7cbb2a04/Userland/Libraries/LibRegex/RegexParser.cpp#L399)。 ### 结论 浏览众多开源操作系统的代码确实乐趣无穷。看到各种实现以及它们在可读性上的巨大差异很有意思。macOS 中的 TRE 是最难理解的,而 musl 一如既往地清晰。FreeBSD 虽然更复杂,但仍然相对直接。 最终结论是,这虽是一个非标准化扩展,但得到了较为广泛的支持,不过并非所有系统都支持。因此,当需要扩展功能时,最好使用 `-E` 或类似选项显式启用扩展正则表达式。

相似文章

可在‘各处’工作的正则表达式

Hacker News Top

本文讨论了正则表达式在sed、awk、grep和Emacs等工具之间移植的挑战,并提供了一组在这些环境中可靠工作的正则表达式子集。

GCC 16及以后版本中的BPF支持

Lobsters Hottest

何塞·马奇西(José Marchesi)和GCC-BPF团队提供了GCC 16中BPF支持的更新,突出了在与LLVM功能对等方面取得的进展,以及内核BPF自测通过率的提升。

关于C扩展、可移植性和替代编译器

Lobsters Hottest

本文讨论了编写可移植C代码的实际挑战,这些挑战源于对非标准编译器扩展和glibc条件头文件的依赖,并通过构建C编译器的示例进行说明。