Как вызвать 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. Любое предложение?

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.

  • Используйте JQ для генерации CSV из поиска ножей
  • jq возвращает полный результат после операции
  • Использование JQ для получения простого разделительного вывода
  • Почему я не могу удалить эти элементы массива в jq?
  • Объединить jq-выход в строку с разделителями-запятыми
  • Разделить поле жала в массив в jq?
  • Подстановочный знак в jq со сравнительными данными
  • Linux и Unix - лучшая ОС в мире.