mongoexport - 叶级别 - JSON 到 CSV 转换 - egrep 不能使用“|”处理多个模式管道或使用 -f 选项

mongoexport - Leaf Level - JSON to CSV conversion - egrep not working with multiple patterns using "|" pipe or with -f option

为什么 egrep 没有给我所有匹配的条目?

这是我的简单 JSON blob:

[nukaNUKA@dev-machine csv]$ cat jsonfile.json
{"number": 303,"projectName": "giga","queueId":8881,"result":"SUCCESS"}

这是我的花样文件(以免吓到小编):

[nukaNUKA@dev-machine csv]$ cat egrep-pattern.txt
\"number\":.*\"projectName
\"projectName\":.*,\"queueId
\"queueId\":.*,\"result
\"result\":\".*$

这是用于单个搜索的 egrep/grep 命令,有效!:

[nukaNUKA@dev-machine csv]$ egrep -o "\"number\":.*\"projectName" jsonfile.json
"number": 303,"projectName
[nukaNUKA@dev-machine csv]$ egrep -o "\"projectName\":.*,\"queueId" jsonfile.json
"projectName": "giga","queueId
[nukaNUKA@dev-machine csv]$ egrep -o "\"queueId\":.*,\"result" jsonfile.json
"queueId":8881,"result
[nukaNUKA@dev-machine csv]$ egrep -o "\"result\":\".*$" jsonfile.json
"result":"SUCCESS"}

那么,为什么这不起作用?我不戴眼镜,是的。

[nukaNUKA@dev-machine csv]$ egrep -o "\"number\":.*\"projectName|\"projectName\":.*,\"queueId|\"queueId\":.*,\"result|\"result\":\".*$" jsonfile.json
"number": 303,"projectName
"queueId":8881,"result
[nukaNUKA@dev-machine csv]$ egrep -o -f egrep-pattern.txt jsonfile.json
"number": 303,"projectName
"queueId":8881,"result
[nukaNUKA@dev-machine csv]$

我有一个 complex 嵌套 JSON blob,因为一切都是非结构化的,所以我不能使用 JQ or JSONV 或任何其他 Python 脚本(因为我要查找的数据存储在包含 1 个字典条目 (key=value) 的数组中,这些条目具有与我要查找的内容相同的键名(例如:{ "parameters": [ { "name": "jobname", "value": "shenzi" }, { "name": "pipelineVersion", "value": "1.2.3.4" }, ...so on..., ... ])和jobname 和 pipelineVersion 或类似参数名称的索引不在我拥有的每个 JSON 条目中的相同索引 [X] 位置。

最坏情况,我可以添加条件检查以查看每个索引处的键是否匹配、作业名称等,然后我得到我要查找的那些字段,但是,然后,我想抓住数百个这样的领域。 如果可能的话,我不想对这些进行硬编码。

我想我的 JSON 条目是每行的,我可以简单地写一个很酷的模式(我知道很丑)但至少我不需要担心条件代码或只使用 BASH/sed/tr/cut 获得我需要的东西的能力,但似乎 egrep -f or -o ... 没有如上所示工作。

示例 JSON blob 对象(来自一项 Jenkins 作业)。 MongoDB 中的单个 JenkinsJobsBuild 集合中有不同的 Jenkins 构建作业的 JSON blob 条目(每个条目都有不同的 JSON 结构、参数等)。 请参阅附件示例 JSON blob 对象。

{
  "_id": {
    "$oid": "5120349es967yhsdfs907c4f"
  },
  "actions": [
    {
      "causes": [
        {
          "shortDescription": "Started by an SCM change"
        }
      ]
    },
    {

    },
    {
      "oneClickDeployPossible": false,
      "oneClickDeployReady": false,
      "oneClickDeployValid": false
    },
    {

    },
    {

    },
    {

    },
    {
      "cspec": "element * ...\/MyProject_latest_int\/LATESTnelement * ...\/MyProject_integration\/LATESTnelement \/vobs\/some_vob\/gigi \/main\/myproject_integration\/MyProject_Slot_0_maint_int\/LATESTnelement * ...\/myproject_integration\/LATESTnelement \/vobs\/some_vob \/main\/LATEST",
      "latestBlsOnConfiguredStream": null,
      "stream": null
    },
    {

    },
    {
      "parameters": [
        {
          "name": "CLEARCASE_VIEWTAG",
          "value": "jenkins_MyProject_latest"
        },
        {
          "name": "BUILD_DEBUG",
          "value": false
        },
        {
          "name": "CLEAN_BUILD",
          "value": true
        },
        {
          "name": "BASEVERSION",
          "value": "7.4.1"
        },
        {
          "name": "ARTIFACTID",
          "value": "lowercaseprojectname"
        },
        {
          "name": "SYSTEM",
          "value": "myprojectSystem"
        },
        {
          "name": "LOT",
          "value": "02"
        },
        {
          "name": "PIPENUMBER",
          "value": "7.4.1.303"
        }
      ]
    },
    {

    },
    {

    },
    {
      "parameters": [
        {
          "name": "DESCRIPTION_SETTER_DESCRIPTION",
          "value": "lowercaseprojectname_V7.4.1.303"
        }
      ]
    },
    {

    },
    {

    },
    {

    },
    {

    }
  ],
  "artifacts": [

  ],
  "building": false,
  "builtOn": "servername",
  "changeSet": {
    "items": [
      {
        "affectedPaths": [
          "vobs\/some_vob\/myproject\/apps\/app1\/Java\/test\/src\/com\/giga\/highlevelproject\/myproject\/schedule\/validation\/SomeActivityTest.java"
        ],
        "author": {
          "absoluteUrl": "http:\/\/11.22.33.44:8080\/user\/hitj1620",
          "fullName": "name1, name2 A"
        },
        "commitId": null,
        "date": {
          "$numberLong": "1489439532000"
        },
        "dateStr": "13\/03\/2017 21:12:12",
        "elements": [
          {
            "action": "create version",
            "editType": "edit",
            "file": "vobs\/some_vob\/myproject\/apps\/app1\/Java\/test\/src\/com\/giga\/highlevelproject\/myproject\/schedule\/validation\/SomeActivityTest.java",
            "operation": "checkin",
            "version": "\/main\/MyProject_latest_int\/2"
          }
        ],
        "msg": "",
        "timestamp": -1,
        "user": "user111"
      }
    ],
    "kind": null
  },
  "culprits": [
    {
      "absoluteUrl": "http:\/\/11.22.33.44:8080\/user\/nuka1620",
      "fullName": "nuka, Chuck"
    }
  ],
  "description": "lowercaseprojectname_V7.4.1.303",
  "displayName": "#303",
  "duration": 525758,
  "estimatedDuration": 306374,
  "executor": null,
  "fullDisplayName": "MyProject \u00bb MyProject-build #303",
  "highlevelproject_metrics_source_url": "http:\/\/11.22.33.44:8080\/job\/MyProject\/job\/MyProject-build\/303\/\/api\/json",
  "id": "303",
  "keepLog": false,
  "number": 303,
  "projectName": "MyProject-build",
  "queueId": 8201,
  "result": "SUCCESS",
  "timeToRepair": null,
  "timestamp": {
    "$numberLong": "1489439650307"
  },
  "url": "http:\/\/11.22.33.44:8080\/job\/MyProject\/job\/MyProject-build\/303\/"
}

当正则表达式在文件中时,您不必转义双引号;你不必为让你的双引号超过 shell.

而奋斗
"number":.*"projectName
"projectName":.*,"queueId
"queueId":.*,"result
"result":".*$

修复后,我得到:

$ egrep -o -f egrep-pattern.txt jsonfile.json 
"number": 303,"projectName
"queueId":8881,"result
$

我认为现在的问题是,您已经用第一个模式消耗了 projectName,所以其他人没有机会匹配它。更改模式以读取逗号,您可以获得更好的结果:

"number":[^,]*
"projectName":[^,]*
"queueId":[^,]*
"result":".*$

产量:

"number": 303
"projectName": "giga"
"queueId":8881
"result":"SUCESS"}

您可以尝试更精致,但您很快就会达到 JSON-aware 工具变得更明智的地步。例如,字符串值中的逗号会弄乱修改后的正则表达式。 (所以,如果项目名称是 "Giga, if not Tera",你就会遇到问题。)


匹配更通用的 JSON name:value 表示法

只要您正在寻找简单的 "key":"quoted value" 对象,就可以使用以下 grep -E(又名 egrep)命令:

grep -Eoe '"[^"]+":"((\(["\/bfnrt]|u[0-9a-fA-F]{4}))|[^"])*"' data

给定 JSON-like 数据(在名为 data 的文件中):

{"key1":"value","key2":"value2 with \"quoted\" text","key3":"value3 with \ and \/ and \f and \uA32D embedded"}

该脚本产生:

"key1":"value"
"key2":"value2 with \"quoted\" text"
"key3":"value3 with \ and \/ and \f and \uA32D embedded"

您可以升级它以处理几乎所有有效的 JSON "key":value,方法是:

grep -Eoe '"[^"]+":(("((\(["\/bfnrt]|u[0-9a-fA-F]{4}))|[^"])*")|true|false|null|(-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?))' data

新的 data 文件包含:

{"key1":"value","key2":"value2 with \"quoted\" text"}
{"key3":"value3 with \ and \/ and \f and \uA32D embedded"}
{"key4":false,"key5":true,"key6":null,"key7":7,"key8":0,"key9":0.123E-23}
{"key10":10,"key11":3.14159,"key12":0.876,"key13":-543.123}

脚本生成:

"key1":"value"
"key2":"value2 with \"quoted\" text"
"key3":"value3 with \ and \/ and \f and \uA32D embedded"
"key4":false
"key5":true
"key6":null
"key7":7
"key8":0
"key9":0.123E-23
"key10":10
"key11":3.14159
"key12":0.876
"key13":-543.123

您可以按照 http://json.org 中 JSON 规范中的铁路图来了解我是如何创建正则表达式的。

它可以通过在允许但不需要空格的地方明智地添加 [[:space:]]* 来增强——在键字符串之前、冒号之前、冒号之后(您也可以在值之后添加它,但你可能不想要那个)。

我采取的另一个简化是密钥不允许使用值字符串所使用的各种转义字符。你可以重复一遍。

当然,这只适用于 'leaf' name:value 对;如果值本身是对象 {…} 或数组 […],则不会将值作为一个整体来处理。

但是,这只是强调它很快就会变得非常混乱,您最好使用 special-purpose JSON 查询工具。正如在对主要查询的评论中提到的那样,其中一种工具是 jq

我拥有的复杂 JSON blob 来自我在 MongoDB 数据库中拥有的 Jenkins(即 Jenkins 作业的 RestAPI 数据)。

为了从 MongoDB 中获取它,我使用 mongoexport 命令成功生成了(non-JsonArray 或 non-Pretty 格式)JSON blob。

#/bin/bash
server=localhost
collectionFile=collections.txt
## Generate collection file contains all collections in the Jenkins database in MongoDB.
( set -x
  mongo "mongoDbServer.company.com/database_Jenkins" --eval "rs.slaveOk();db.getCollectionNames()" --quiet > ${collectionFile}
)
## create collection based JSON files
for collection in $(cat ${collectionFile} | sed -e 's:,: :g')
do
  mongoexport --host ${server} --db ${db} --collection "${collection}" --out ${exportDir}/${collection}.json
  ##mongoexport --host ${server} --db ${db} --collection "${collection}" --type=csv --fieldFile ~/mongoDB_fetch/get_these_csv_fields.txt --out ${exportDir}/${collection}.csv; ## This didn't work if you have nested fields. fieldFile file was just containing field name per line in a particular xyz.IndexNumber.yyy format.
done

尝试使用 -f 字段的内置 mongoexport 命令 --type=csv 来捕获 topfield.0.subField, field2, field3.7.parameters.7.. 没有任何效果。

PS:如果要创建 CSV 文件,则 . 标记后的 number 是定义索引的方式,并且使用字段(强制)使用 mongoexport 命令。

因为我的 JSON 结构都是非结构化的(Jenkins 版本 bumps/upgrades 发生在过去并且关于工作的数据不是相同的结构),我尝试了这个最后的 sed 技巧(因为 JSON 每个条目的数据在每一行中)。

sed 命令(如下所示)将为您提供所有 它的值 (在 key=value 格式)每行 在 LEAF 字段 key=value 级别 几乎任何 JSON blob / 至少来自 Jenkins JSON blob。获得此信息后,您可以将此命令的输出提供给临时文件,然后读取所有值部分(在 = 标记之后)并创建您的 CSV 文件 acc。是的,您必须对其进行排序,以便按 header 名称的顺序维护 CSV 文件的字段,从而将值插入右侧 column/field。我从所有不同的 collection JSON 文件的临时 key=value 生成的键名中计算出字段名。然后,读取所有临时 collection 文件并添加值 acc。进入各自 header/field/column.

下的最终 CSV 文件

好的,这是一个奇怪的解决方案,但至少它是一个解决方案 - 在 one liner.

cat myJenkinsJob.json | sed "s/{}//g;s/,,*/,/g;s/},\"/\n/g;s/},{/\n/g;s/\([^\"a-zA-Z]\),\"/\n/g;s/:\[{/\n/g;s/\"name\":\"//g;s/\",\"value//g;s/,\"/\n/g;s/\":\"*/=/g;s/\"//g;s/[\[}\]]//g;s/[{}]//g;s/$[a-zA-Z][a-zA-Z]*=//g"|grep "=" | sed "s/,$//"|egrep -v "=-|=$|=\[|^_class="

调整这个acc。如果您的 JSON blob 向您显示了您不想要的有趣字符,请稍微调整一下您自己的 sed 部分解决方案。下面的 sed 操作顺序很重要。我还排除了任何冗余变量(我现在不需要,例如:JSON blob contained _class="..." values)所以我排除了那些通过 egrep -v在最后一个 | 管道之后。