Почему 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..., ... ]), и индекс для имени задания и pipelineVersion или аналогичных имен параметров не находится в одном и том же месте индекса [ X ] во всех имеющихся у меня записях JSON.

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

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

Пример объекта BLOB-объекта JSON (из одного задания Jenkins). В одной коллекции JenkinsJobsBuild в MongoDB есть разные записи JSON для задания сборки Jenkins (каждая из которых имеет разные структуры JSON, параметры и т. Д.). См. Прикрепленный образец объекта BLOB-объекта 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\/"
}
0
Arun Sangal 13 Мар 2018 в 03:19

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-символы, которые делает строка значения. Вы можете повторить это.

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

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

1
Jonathan Leffler 13 Мар 2018 в 16:18

Сложный JSON blob, который у меня был, был взят из Jenkins (например, Jenkins job Rest API data), который у меня был в базе данных MongoDB.

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

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

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

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

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

Эта команда sed (как показано ниже) предоставит вам все ключи и их значения (в формате ключ = значение ). на строку на уровне поля LEAF ключ = значение почти любого большого двоичного объекта JSON / по крайней мере из большого двоичного объекта 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-объект JSON содержал значения _class = "..."), поэтому я исключаю их через egrep -v после последнего { {X2}} труба.

1
AKS 13 Мар 2018 в 19:18