Как вызвать jq внутри awk?

По существу у меня есть file.log, как следует

blah blah blah blah Hello world | {"foo": "bar"} blah blah Hello earth | {"foo1": "bar1"} 

Теперь моя цель состоит в том, чтобы написать некоторые команды оболочки, чтобы получить выход желания следующим образом:

 Hello earth | "bar" Hello earth | "bar1" 

В настоящее время это то, что у меня есть:

 grep Hello file.log | awk -F "|" '{print $1, system("jq " $2)}' 

Однако вызов jq дает мне эту ошибку:

 jq: error: syntax error, unexpected ':', expecting $end (Unix shell quoting issues?) at <top-level>, line 1: bin:application jq: 1 compile error 

Я думаю, что из-за того, что внутри system () мои 12 $ лишены всех символов котировки ("), таким образом JQ не может распознать его json. Любое предложение?

  • Установка вывода jq в переменную Bash
  • Редактирование значения дочернего элемента JSON с помощью jq
  • Почему я не могу удалить эти элементы массива в jq?
  • jq возвращает полный результат после операции
  • равное сравнение по переменной jq
  • Повторение текста и выполнение команды с помощью jq
  • Не удалось выполнить конкатенацию строки bash
  • bash добавить свойство, используя переменную с jq
  • 2 Solutions collect form web for “Как вызвать jq внутри awk?”

    У вас здесь несколько проблем

    • system не возвращает что-то для печати, она возвращает значение выхода команды, которую вы выполнили (0, если все выполнено штрафом). Вы увидите ваши JSON-декодированные данные, а затем строку, такую ​​как Hello earth 0
    • двойные кавычки в вашей строке JSON проглатываются оболочкой. Результирующая команда, которую вы выполняете, – jq {foo: bar} (два аргумента, JSON больше не цитируется)
    • если $2 содержит специальные символы, такие как $ , ваша оболочка будет их интерпретировать
    • даже при правильном цитировании jq не называется подобным, он ожидает, что фильтр будет первым аргументом (например, « . »), и он ожидает, что вход JSON будет считан из файла или со стандартного ввода
    • построение команды из журналов и выполнение ее имеет огромные последствия для безопасности (что, если $2 было ; rm -rf ~ ?). Лучше избегайте этого, если сможете.

    Проблема безопасности, отложенная, представляет собой awk код, который будет работать большую часть времени:

     awk -F "|" '{ printf "%s", $1; system("echo \x27" $2 "\x27 | jq .")}' 

    То, что он делает, – отправить $2 в одиночные кавычки ( \x27 ) в jq через stdin.

    Проблемы остаются, хотя

    • если $2 содержит одиночную кавычку, она разбивает всю команду
    • если $2 начинается с тире (маловероятно), это будет интерпретироваться как опция для echo (мы можем использовать команду printf вместо echo )
    • проблема безопасности уже упоминалась (например, если $2 содержит ...'; rm -r ~; : ' ... любом месте строки)

    Теперь лучший код awk

     awk -F "|" '{ printf "%s", $1; print $2 | "jq ."; close("jq ."); }' 

    Поскольку $2 отправляется в jq процесс через stdin, но теперь, используя awk канал, он больше не интерпретируется оболочкой, решая все вышеперечисленные проблемы. Команда jq должна быть закрыта (завершена) в каждой строке, поэтому вызов close() .

    xhienne дал хороший обзор проблем с существующим кодом и хорошей альтернативой тому, что вы хотите достичь.

    Ниже приведена другая альтернатива: не пытайтесь вызвать jq из awk вообще, но пусть awk скрипт создает правильный вывод JSON.

     $ awk -F '|' 'BEGIN { print "[" } $2 { if (t) print t ","; t = $2 } END { print t, "]" }' file |jq . [ { "foo": "bar" }, { "foo1": "bar1" } ] 

    Код awk сам по себе будет генерировать следующий массив JSON из найденных объектов JSON (с учетом примера в вопросе):

     [ {"foo": "bar"}, {"foo1": "bar1"} ] 

    Это позволяет вам работать более свободно с jq не делая ваш скрипт слишком сложным для поддержания и понимания.

    Жонглирование переменной t в скрипте – это просто способ убедиться, что мы не получаем конечную запятую после последнего объекта JSON.

    Linux и Unix - лучшая ОС в мире.