mongoexport - Leaf Level - преобразование JSON в CSV - egrep не работает с несколькими шаблонами, используя "|" труба или с опцией -f

Зачем egrep не дает мне все соответствующие записи?

Это мой простой JSON-объект:

[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]$

У меня есть сложный вложенный большой двоичный объект JSON, и, поскольку все неструктурировано, кажется, что я не могу использовать JQ или JSONV или что-либо другое сценарий Python (поскольку данные, которые я ищу, хранятся в массивах, содержащих 1 словарные записи (ключ = значение) с теми же именами ключей для того, что я ищу (например: { "parameters": [ { "name": "jobname", "value": "shenzi" }, { "name": "pipelineVersion", "value": "1.2.3.4" }, ...so on..., ... ]) и индекс для имени задания и pipeVersion или аналогичных имён параметров не находится в одном и том же месте индекса [X] в каждой записи JSON, которую я имею.

В худшем случае, я могу добавить условные проверки, чтобы увидеть, совпадает ли ключ в каждом индексе, имя задания и т. Д., А затем я получаю те поля, которые я ищу, но есть сотни таких полей, которые я хочу получить. Я не хочу жестко их кодировать, если это возможно.

Я думал, что моя запись JSON для каждой строки, я могу просто написать классные шаблоны (безобразно, я знаю), но по крайней мере тогда мне не нужно беспокоиться об условном коде или просто использовать мощность BASH/sed/tr/cut для получения что мне нужно но похоже egrep -f or -o ... не работает, как показано выше.

Пример объекта BLOB-объекта JSON (из одного задания Дженкинса). В одной коллекции JenkinsJobsBuild в MongoDB есть разные записи JSON для создания BLOB-объектов JSON (каждая имеет разные структуры JSON, параметры и т. Д.). См. Прикрепленный образец объекта JSON.

{
  "_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\/"
}

2 ответа

Решение

Когда регулярные выражения находятся в файле, вам не нужно экранировать двойные кавычки; вам не нужно сражаться, чтобы получить двойные кавычки за раковиной.

"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 станет более разумным. Например, запятые в строковом значении могут испортить измененные регулярные выражения. (Итак, если бы проект назывался "Гига, если не Тера", у вас были бы проблемы.)


Соответствие более общему имени JSON: нотация значения

Пока вы ищете простой "key":"quoted value" объекты, вы можете использовать следующие grep -E (ака egrep) команда:

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

Учитывая JSON-подобные данные (в файле под названием 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

Вы можете следовать железнодорожным схемам в спецификации JSON схемы на http://json.org/ чтобы увидеть, как я создал регулярное выражение.

Это может быть усилено разумным добавлением [[:space:]]* в местах, где пробелы разрешены, но не обязательны - до ключевой строки, до двоеточия, после двоеточия (вы могли бы добавить его и после значения, но, вероятно, вам это не нужно).

Другое упрощение, которое я принял, заключается в том, что ключ не учитывает различные escape-символы, которые делает строка значения. Вы могли бы повторить это.

И, конечно же, это работает только для "листьев" name: value пары; если значение само является объектом {…} или массив […] это не обрабатывает значение в целом.

Тем не менее, это просто подчеркивает, что это очень быстро запутывается, и вам лучше использовать специальный инструмент запросов JSON. Одним из таких инструментов является jq, как упоминалось в комментарии к основному запросу.

У меня был сложный BLOB-объект JSON от Дженкинса (то есть данные RestAPI работы Дженкинса), который был у меня в базе данных MongoDB.

Чтобы получить его из MongoDB, я использовал команду mongoexport для генерации (не JsonArray или не Pretty) блоба JSON успешно.

#/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

Пробовал встроенную команду монгоэкспорта --type=csv с -f поля для ловли topfield.0.subField, field2, field3.7.parameters.7.. ничего не получалось.

PS: number после . mark - это способ определения индексов, если вы собираетесь создать файл CSV и использовать поля (обязательные), используя mongoexport команда.

Поскольку вся моя структура JSON была неструктурированной (выпады / обновления версии Jenkins происходили в прошлом, а данные о задании не совпадали), я попробовал это окончательно sed трюк (так как данные JSON на запись были в каждой отдельной строке).

это sed Команда (как показано ниже) выдаст вам все ключи и их значения (в формате ключ = значение) на строку в поле LEAF ключ = уровень значения практически любого BLSON-объекта / по крайней мере из BLOB- объекта Jenkins JSON. Получив эту информацию, вы можете передать выходные данные этой команды во временный файл, а затем прочитать всю часть значения (после = Отметьте) и создайте файл CSV в соотв. ДА, вы должны отсортировать его так, чтобы поля вашего CSV-файла сохранялись в порядке имен заголовков и, таким образом, значения вставлялись в правый столбец / поле. Я вычислил имена полей из временного ключа всего разного набора JSON-файла = значения сгенерированного ключа. Затем прочитайте все временные файлы коллекции и добавьте значения в соотв. в окончательный файл CSV под соответствующим заголовком / полем / столбцом.

ОК, это странное решение, но, по крайней мере, это решение - в одном лайнере.

cat myJenkinsJob.json | sed "s/{}//g;s/,,*/,/g;s/},\"/\n/g;s/},{/\n/g;s/\([^\"a-zA-Z]\),\"/\1\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="

Настроить это в соотв. к вашему собственному решению для sed немного, если ваш шарик JSON показывает забавные символы, которые вам не нужны. Порядок операций sed ниже важен. Я также исключаю любые избыточные переменные (которые мне сейчас не нужны, например, BLOB-объект ex: JSON содержал значения _class="..."), поэтому я исключаю их через egrep -v после последнего | труба.

Другие вопросы по тегам