使用sed命令解析json文本的正则表达式

use*_*040 15 command-line regex sed text-processing

我有这个json文本:

{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}
Run Code Online (Sandbox Code Playgroud)

我想提取buildStatus的整体状态,即预期的输出是“ERROR”

"buildStatus" : {
    "status" : "ERROR",
    ....
}
Run Code Online (Sandbox Code Playgroud)

我尝试了下面的 sed 表达式,但它不起作用,它返回OK

status= sed -E 's/.*\"buildStatus\":.*\"status\":\"([^\"]*)\",.*/\1/' jsonfile
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Byt*_*der 16

不要使用正则表达式解析复杂的嵌套数据结构,如 JSON 或 XML,使用适当的 JSON 解析器,如jshon.

首先你需要安装它:

sudo apt-get install jshon
Run Code Online (Sandbox Code Playgroud)

然后,您必须通过标准输入向其提供要解析的 JSON 数据,以便您可以使用管道 ( |)将另一个命令的输出重定向到那里,或者将文件重定向到它 ( < filename)。

提取所需数据所需的参数如下所示:

jshon -e "buildStatus" -e "status" -u
Run Code Online (Sandbox Code Playgroud)
  • -e "buildStatus" 从顶级字典中选择具有“buildStatus”索引的元素。
  • -e "status" 从上面选择的第二级字典中选择具有“状态”索引的元素。
  • -u 将选定的数据从 JSON 转换为纯数据(即在这里它删除了字符串周围的引号)

因此,根据您从何处获取数据,您运行的命令看起来像以下命令之一:

jshon -e "buildStatus" -e "status" -u < YOUR_INPUT_FILE
YOUR_JSON_PRODUCING_COMMAND | jshon -e "buildStatus" -e "status" -u
Run Code Online (Sandbox Code Playgroud)

要了解更多信息jshon,您可以阅读其在线访问的联机帮助页或者只需键入man jshon

  • 还有 [`jq`](http://packages.ubuntu.com/trusty/jq):`jq -r .buildStatus.status` (6认同)
  • [当您尝试使用正则表达式解析非常规语言时会发生这种情况。](http://stackoverflow.com/a/1732454/5684257) (2认同)

hee*_*ayl 10

工作jq

jq -r '.["buildStatus"]["status"]' file.json
Run Code Online (Sandbox Code Playgroud)

可以缩短为:

jq -r '.buildStatus.status' file.json
Run Code Online (Sandbox Code Playgroud)

-r( --raw-output) 输出没有json字符串格式的字符串,即没有引号。

例子:

% cat file.json                   
{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

% jq -r '.["buildStatus"]["status"]' file.json
ERROR

% jq -r '.buildStatus.status' file.json       
ERROR
Run Code Online (Sandbox Code Playgroud)

如果尚未安装,请通过以下方式安装(在 Universe 存储库中可用):

sudo apt-get install jq 
Run Code Online (Sandbox Code Playgroud)


Ser*_*nyy 8

如前所述,最好使用适当的 API 来解析复杂的结构化数据。Python 有一个json模块,我个人在我的脚本中使用了很多,并且很容易提取所需的字段,如下所示:

$ python -c 'import sys,json;print json.load(sys.stdin)["buildStatus"]["status"]' <  input.txt
ERROR
Run Code Online (Sandbox Code Playgroud)

这里发生的事情是我们将输入文件重定向到 python 的标准输入,并使用json.load(). 这变成了一个带有“buildStatus”键的python字典,它包含另一个带有“status”键的python字典。因此,我们只是打印出存储在另一个字典中的字典中键的值。相当简单。

除了简单之外,另一个优点是 python 和这个 API 都是预装的,并且默认随 Ubuntu 一起提供。


ter*_*don 6

真正做到这一点的sed,但我强烈建议你使用具有写入处理JSON数据的工具更复杂的语言。例如,您可以尝试使用 perl 或 python。

现在,在您的简单示例中,您想要的只是第一次出现"status",因此您可以执行以下操作:

$ sed -nE '/status/{s/.*:\s*"(.*)",/\1/p;q}' file.json 
ERROR
Run Code Online (Sandbox Code Playgroud)

诀窍是使用-n以避免打印,然后如果该行匹配status( /status/),则删除除所需部分之外的所有内容s/.*:\s*"(.*)",/\1/p打印该行并进行打印q


就个人而言,我发现这个等效的 grep 命令要简单得多:

$ grep -m1 -oP '"status"\s*:\s*"\K[^"]+' file.json 
ERROR
Run Code Online (Sandbox Code Playgroud)

或者这个:

$ perl -ne 'if(s/.*"status"\s*:\s*"([^"]+).*/$1/){print;exit}' file.json 
ERROR
Run Code Online (Sandbox Code Playgroud)

不过说真的,如果您打算解析 JSON 文件,请不要尝试手动执行此操作。使用合适的 JSON 解析器。


Zan*_*nna 6

不是说你应该使用sed(我认为有人因为没有写强制性警告而对我投反对票)但是,如果你需要在下一行搜索一些东西,buildStatus因为你似乎在尝试自己的尝试,你需要告诉sed阅读N命令的下一行

$ sed -rn '/buildStatus/N;s/.*buildStatus.*\n.*: "(.*)",/\1/p' file
ERROR
Run Code Online (Sandbox Code Playgroud)

笔记:

  • -n 在我们要求之前不要打印任何东西
  • -r使用 ERE(与 相同-E
  • /buildStatus/N 找到这个模式并阅读下一行
  • s/old/new/替换oldnew
  • .* 任意数量的任意字符
  • \n 新队
  • : "(.*)",保存在: "和之间出现的任何字符",
  • \1 反向引用保存的模式
  • p 打印我们处理过的部分