Распечатайте некоторые значения на основе условий из всех файлов JSON

У меня есть несколько файлов JSON в папке и подпапках, для которых я хочу напечатать только 3 поля. Я использую цикл for для всех файлов json, но для простоты входные данные ниже представляют собой 3 файла json. Для каждого из них я хочу печатать «Имя файла» и «Значение» только тогда, когда внутри каждого файла появляется хотя бы один «Dmo = Path». Если файлы не содержат блока «Dmo = Path», печатайте только имя файла.

{
  "Filename": "File_213",
  "Date": "2024-4-30",
  "Blocks": [
    {
      "Dmo": "WW",
      "Value": "23",
      "String": "",
    },
    {
      "Dmo": "Path",
      "Value": "/Files/2024/abd",
      "String": "",
    },
    {
      "Dmo": "Path",
      "Value": "/Files/2024/Ndew",
      "String": "",
    }
  ]
}

{
  "Filename": "File_4",
  "Date": "2024-4-30",
  "Blocks": [
    {
      "Dmo": "WW",
      "Value": "45",
      "String": "",
    }
  ]
}

{
  "Filename": "File_43",
  "Date": "2024-4-30",
  "Blocks": [
    {
      "Dmo": "Path",
      "Value": "/Files/2023/Roi2",
      "String": "",
    }
    }
  ]
}

Мой текущий код и текущий вывод, как показано ниже

$ awk '/"Filename":/{fnm=$2}
    /Dmo/{dmo=$2}
        /Value/ {

        val=$2;

    if (dmo != "")
        print fnm,val
    else
        print fnm

    fnm = ""; dmo = "";val = ""}' input

"File_213", "23",
 "/Files/2024/abd",
 "/Files/2024/Ndew",
"File_4", "45",
"File_43", "/Files/2023/Roi2",

Мой ожидаемый результат:

File_213, /Files/2024/abd
File_213, /Files/2024/Ndew
File_4
File_43, /Files/2023/Roi2
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

jq лучше обрабатывать файлы JSON, чем awk.

for j in *.json ; do
    jq -r '[.Filename, [.Blocks[] | select(.Dmo= = "Path")]] as [$f, $p]
           | if $p[0]
             then ($p[].Value | [$f, .])
             else [$f]
           end | @csv' < "$j" ; done

Выход:

"File_213","/Files/2024/abd"
"File_213","/Files/2024/Ndew"
"File_4"
"File_43","/Files/2023/Roi2"

@спасибо за помощь, это работает, я пытаюсь адаптировать его к своим реальным данным, но, похоже, работает.

Rasec Malkic 01.05.2024 05:06
Ответ принят как подходящий

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

jq -r '
  if any(.Blocks[]; .Dmo == "Path")
  then .Filename + (.Blocks[] | ", " + select(.Dmo == "Path").Value)
  else .Filename end
' input
  • .Filename, .Blocks, .Dmo и .Value получают доступ к соответствующим значениям полей и с прикрепленным [] перебирают значения этого массива.
  • any выдает true, если хотя бы одно из предоставленных значений соответствует заданному условию, а select фильтрует входные значения в соответствии с заданным условием.
  • Флаг -r декодирует результаты строки JSON в необработанный текст (по сути, удаляя двойные кавычки).

Это производит

File_213, /Files/2024/abd
File_213, /Files/2024/Ndew
File_4
File_43, /Files/2023/Roi2

Демо

В качестве альтернативы можно дедуплицировать часть кода, преобразовав массив .Blocks в массив с заранее сгенерированными значениями результатов, а затем проверить его на пустоту, используя переменные для справки:

jq -r '
  [.Blocks[] | ", " + select(.Dmo == "Path").Value] as $a
  | .Filename + if $a == [] then "" else $a[] end
' input

Демо

Другой вариант — использовать оператор // для создания альтернативного значения, которое срабатывает при отсутствии входных данных при итерации по предварительно сгенерированному массиву. (Это основано на очевидном вторичном ограничении, согласно которому все значения .Value на самом деле являются строками, т. е. не false или null.)

jq -r '.Filename + (.Blocks | map(", " + select(.Dmo == "Path").Value)[] // "")'

Демо

он отлично работает и краткий сценарий. Поскольку в моем случае речь идет об обработке нескольких файлов в папке и подпапках, как добавить путь к текущему обрабатываемому файлу? это должно быть вне команды jq? Моя конечная цель - получить представление файлов json в виде «дерева» на основе местоположения файлов и Paths, отображаемого в каждом файле.

Rasec Malkic 01.05.2024 05:05

@Rasec Просто замените .Filename на input_filename (без предшествующей точки!) в любом из приведенных выше примеров, чтобы получить путь к обрабатываемому в данный момент входному файлу. Если вы хотите удалить имя файла (просто сохраните путь к содержащей его папке), используйте строковые функции, чтобы удалить все, начиная с последнего символа-разделителя пути /, например. (input_filename | sub("/[^/]+$"; "")).

pmf 01.05.2024 05:07

Я перешел на jq -r 'input_filename + (.Blocks | map(", " + select(.Dmo == "Path").Value)[] // "")' file для обработки одного файла, и на выходе отображается имя файла без полного пути, вот так filename, /Files/2024/abd. Затем, если я запущу эту команду внутри цикла for, вместо имени файла с путем я получу <stdin>, /Files/2024/abd

Rasec Malkic 01.05.2024 06:37

@Rasec input_filename создает имя, как указано (попробуйте jq … dir1/dir2/file), или "<stdin>", если ввод поступает из STDIN (он был перенаправлен < или передан по конвейеру | в jq, а не в имя файла arg). Вообще говоря, вы, вероятно, можете переместить цикл оболочки в jq, чтобы вам не приходилось перенаправлять/передавать какие-либо данные (и это также было бы более эффективно из-за меньшего количества вызовов из оболочки). Альтернативно, используйте опцию --arg jq для импорта одного значения (имени файла), которое вы можете использовать в структуре JSON для сохранения в качестве реальных данных, даже если фактическая ссылка на файл потеряна из-за перенаправления/конвейера.

pmf 01.05.2024 06:54

Спасибо. Это работает с использованием jq '...' file вместо jq '...' < file.

Rasec Malkic 01.05.2024 07:26

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