MacOS下用grep正则提取大括号间内容的问题求助
我需要从curl的返回结果中提取{}之间的内容,已经在Sublime及在线正则测试工具中验证{([\S\s]+)}、{[^{}]*}这两个正则表达式可以正常工作。但在MacOS的grep(包括brew安装的版本)中,只有移除返回内容的换行符时命令才生效,无法处理带换行的curl返回结果,请问该问题的原因及解决方法是什么?
curl返回示例:
HTTP/2 401 www-authenticate: Digest realm="MMS Public API", domain="", nonce="8878t9jXCP7+", algorithm=MD5, qop="auth", stale=false content-type: application/JSON content-length: 106 x-envoy-upstream-service-time: 3 date: Fri, 13 Jan 2023 17:04:03 GMT server: envoy HTTP/2 400 date: Fri, 13 Jan 2023 17:04:04 GMT strict-transport-security: max-age=31536000; include subdomains; referrer-policy: strict-origin-when-cross-origin x-permitted-cross-domain-policies: none x-content-type-options: nosniff content-type: application/json x-frame-options: DENY content-length: 200 x-envoy-upstream-service-time: 23 server: envoy { "detail": "Cluster asdasdasd cannot be created in a paused state.", "error": 400, "errorCode": "CANNOT_CREATE_PAUSED_CLUSTER", "parameters" : [ "asdasdasd" ], "reason": "Bad Request" }
目标提取内容:
{ "detail": "Cluster asdasdasd cannot be created in a paused state.", "error": 400, "errorCode": "CANNOT_CREATE_PAUSED_CLUSTER", "parameters" : [ "asdasdasd" ], "reason": "Bad Request" }
MacOS自带的BSD grep以及brew安装的GNU grep,默认都是逐行处理输入内容:正则表达式中的.默认不匹配换行符,即便用[\S\s]这类能覆盖换行的字符集,grep依然会按行分割内容后逐一匹配,无法识别跨多行的{}区块。只有把所有换行移除,让内容变成单行,正则才能匹配到完整的{}区间。
方法1:用grep的Perl正则+null分隔符模式
brew安装的GNU grep支持-P启用Perl兼容正则,-z将输入视为以null字符分隔的数据流(把整个输入当作单个字符串处理),结合这两个参数可匹配跨多行内容:
curl <你的请求URL> | grep -Pzo '{[\s\S]+}'
如果需要保留换行格式,再用tr把null字符换回换行:
curl <你的请求URL> | grep -Pzo '{[\s\S]+}' | tr '\0' '\n'
方法2:用awk处理
awk可通过状态标记捕获从{到}的所有内容,不受换行限制:
curl <你的请求URL> | awk '/\{/{flag=1} flag; /\}/{flag=0}'
逻辑:匹配到{时开启标记,输出所有标记开启后的行,直到匹配到}时关闭标记。
方法3:用sed处理
sed的多行匹配模式可跨行提取,注意MacOS自带BSD sed和brew安装的GNU sed语法一致:
curl <你的请求URL> | sed -n '/{/,/}/p'
该命令会输出从第一个{到第一个}之间的所有内容,包含{和}本身。
方法4:用jq(推荐,针对JSON内容)
因为目标内容是JSON格式,用专门的JSON工具jq更可靠,无需正则:
curl <你的请求URL> | jq '.'
jq会自动识别并输出返回结果中的JSON部分,还能格式化输出,比正则更稳定,尤其适合复杂JSON结构。
内容的提问来源于stack exchange,提问作者Felipe Cabral

