【问题标题】:Egrep special expressions like \w in bracket expressions []egrep 特殊表达式,如括号表达式 [] 中的 \w
【发布时间】:2019-03-14 08:19:33
【问题描述】:

我正在尝试使用扩展的 grep 从 JSON 中提取数据。我使用的正则表达式在我的regexr instance 上是有效的,但由于某种原因它在 bash 中不起作用。

我尝试了很多东西,特别是 bare double dash 以及对正则表达式的各种小修改以进行转义。

#!/bin/bash
networks='{ "networks": [ { "admin_state_up": true, "availability_zone_hints": [], "availability_zones": [], "created_at": "2019-03-12T23:45:13Z", "description": "", "id": "7188504a-72cb-4590-a9b0-414732017837", "ipv4_address_scope": null, "ipv6_address_scope": null, "is_default": false, "mtu": 1450, "name": "BLUE", "port_security_enabled": true, "project_id": "187d635aec4c43fe8e8918afb3a5c82e", "provider:network_type": "vxlan", "provider:physical_network": null, "provider:segmentation_id": 86, "revision_number": 2, "router:external": false, "shared": false, "status": "ACTIVE", "subnets": [], "tags": [], "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e", "updated_at": "2019-03-12T23:45:13Z" }, { "admin_state_up": true, "availability_zone_hints": [], "availability_zones": [], "created_at": "2019-03-12T23:45:13Z", "description": "", "id": "ed82083f-0a7c-4322-a4fb-de8db23e2bae", "ipv4_address_scope": null, "ipv6_address_scope": null, "is_default": false, "mtu": 1450, "name": "RED", "port_security_enabled": true, "project_id": "187d635aec4c43fe8e8918afb3a5c82e", "provider:network_type": "vxlan", "provider:physical_network": null, "provider:segmentation_id": 108, "revision_number": 2, "router:external": false, "shared": false, "status": "ACTIVE", "subnets": [], "tags": [], "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e", "updated_at": "2019-03-12T23:45:13Z" }, { "admin_state_up": true, "availability_zone_hints": [], "availability_zones": [], "created_at": "2019-03-12T23:45:13Z", "description": "", "id": "1eb6647e-869e-4e83-9468-43e2c320bccc", "ipv4_address_scope": null, "ipv6_address_scope": null, "is_default": false, "mtu": 1450, "name": "public", "port_security_enabled": true, "project_id": "187d635aec4c43fe8e8918afb3a5c82e", "provider:network_type": "vxlan", "provider:physical_network": null, "provider:segmentation_id": 32, "revision_number": 2, "router:external": false, "shared": false, "status": "ACTIVE", "subnets": [], "tags": [], "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e", "updated_at": "2019-03-12T23:45:13Z" } ] }'
result=`echo $networks | grep -oE '"(id|name)": "([\w+-]+)"'`
echo $result

上述代码不起作用,但如果我切换到以下正则表达式,它就会起作用。我也只需要为 id 字段添加提取,以便能够使用 \2 反向引用(第 2 组)提取 id 和名称

grep -oE '"(id|name)": "(\w+)"'

你能帮我理解为什么脚本不起作用吗?

全格式 JSON

{
  "networks": [{
    "admin_state_up": true,
    "availability_zone_hints": [],
    "availability_zones": [],
    "created_at": "2019-03-12T23:45:13Z",
    "description": "",
    "id": "7188504a-72cb-4590-a9b0-414732017837",
    "ipv4_address_scope": null,
    "ipv6_address_scope": null,
    "is_default": false,
    "mtu": 1450,
    "name": "BLUE",
    "port_security_enabled": true,
    "project_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "provider:network_type": "vxlan",
    "provider:physical_network": null,
    "provider:segmentation_id": 86,
    "revision_number": 2,
    "router:external": false,
    "shared": false,
    "status": "ACTIVE",
    "subnets": [],
    "tags": [],
    "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "updated_at": "2019-03-12T23:45:13Z"
  }, {
    "admin_state_up": true,
    "availability_zone_hints": [],
    "availability_zones": [],
    "created_at": "2019-03-12T23:45:13Z",
    "description": "",
    "id": "ed82083f-0a7c-4322-a4fb-de8db23e2bae",
    "ipv4_address_scope": null,
    "ipv6_address_scope": null,
    "is_default": false,
    "mtu": 1450,
    "name": "RED",
    "port_security_enabled": true,
    "project_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "provider:network_type": "vxlan",
    "provider:physical_network": null,
    "provider:segmentation_id": 108,
    "revision_number": 2,
    "router:external": false,
    "shared": false,
    "status": "ACTIVE",
    "subnets": [],
    "tags": [],
    "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "updated_at": "2019-03-12T23:45:13Z"
  }, {
    "admin_state_up": true,
    "availability_zone_hints": [],
    "availability_zones": [],
    "created_at": "2019-03-12T23:45:13Z",
    "description": "",
    "id": "1eb6647e-869e-4e83-9468-43e2c320bccc",
    "ipv4_address_scope": null,
    "ipv6_address_scope": null,
    "is_default": false,
    "mtu": 1450,
    "name": "public",
    "port_security_enabled": true,
    "project_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "provider:network_type": "vxlan",
    "provider:physical_network": null,
    "provider:segmentation_id": 32,
    "revision_number": 2,
    "router:external": false,
    "shared": false,
    "status": "ACTIVE",
    "subnets": [],
    "tags": [],
    "tenant_id": "187d635aec4c43fe8e8918afb3a5c82e",
    "updated_at": "2019-03-12T23:45:13Z"
  }]
}

【问题讨论】:

  • 完成????我最初认为根本原因是在正则表达式中添加破折号。

标签: regex bash grep


【解决方案1】:

根据man grep

反斜杠字符和特殊表达式

符号\w 是[[:alnum:]] 的同义词,\W 是[^[:alnum:]] 的同义词。 ... 括号表达式是由 [ 和 ] 括起来的字符列表。 ... 要包含文字 ] 将其放在列表的首位。类似地,要包含文字 ^,请将其放在除第一个之外的任何位置。最后,要包含文字 - 将其放在最后。

基本上,\w 在评估时字面意思被替换为这些字符,给你"([[[:alnum:]]+-]+)",在美国标准语言环境中给你"([[a-zA-Z0-9]+-]+)"

由于括号表达式被它看到的第一个] 截断(除非它是括号表达式的第一个元素),所以该组只有[[[:alnum:]]+,或“一个或多个数字、字母和[。这个表达式后面跟着-]+,意思是“正好一个连字符和一个或多个]”。这显然很糟糕。

如果你尝试

echo $networks | grep -oE '"(id|name)": "([[:alnum:]+-]+)"'

\w不带外括号表达式,相关部分表示“由一个或多个数字、字母、连字符和加号组成的一组(由"包围)”,其输出:

"id": "7188504a-72cb-4590-a9b0-414732017837"
"name": "BLUE"
"id": "ed82083f-0a7c-4322-a4fb-de8db23e2bae"
"name": "RED"
"id": "1eb6647e-869e-4e83-9468-43e2c320bccc"
"name": "public"

【讨论】:

【解决方案2】:

使用 PERL (-P) 而不是扩展 (-E) 正则表达式,看起来 \w 被解释为预期的那样,没有转义问题:注意 -oP

result=$( echo $networks | grep -oP '"(id|name)": "([\w+-]+)"' ) ; 
echo $result
"id": "7188504a-72cb-4590-a9b0-414732017837" "name": "BLUE" "id": "ed82083f-0a7c-4322-a4fb-de8db23e2bae" "name": "RED" "id": "1eb6647e-869e-4e83-9468-43e2c320bccc" "name": "public"

【讨论】:

  • 这在我的目标环境中对我有用,但是,-P 在我在 macOS 上测试时不起作用,因为 grep 的版本是 grep (BSD grep) 2.5.1-FreeBSD,而我的目标环境有 grep (GNU grep) 3.1。很高兴知道,再次感谢您的解决方案。
【解决方案3】:

作为一种解决方法(它不能解决“转义\w 问题)

result=$( echo $networks | grep -oE '"(id|name)": "([a-zA-Z_+-]+)"' ) ; 
echo $result

打印我:

"name": "BLUE" "name": "RED" "name": "public"

注意:更喜欢使用$( ) 语法来执行子shell,而不是反引号。

【讨论】:

    猜你喜欢
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2018-04-29
    • 1970-01-01
    • 2021-02-02
    • 2021-03-12
    相关资源
    最近更新 更多