Как написать функцию рыбы, которая делегирует JQ? - PullRequest
0 голосов
/ 28 июня 2018

Я пытаюсь написать функцию-фиш с именем jq_select_keys, которая выбирает подмножество ключей из данного JSON.

Заклинание jq для создания этого магического предмета:

jq -r "with_entries(select([.key] | inside([\"bar\",\"baz\",\"qux\"])))" file.json

Теперь я пытаюсь определить удобную функцию с именем jq_select_keys, которая будет принимать имя файла и ключи, которые меня интересуют, и выплевывать подмножество. Вот что я придумал:

function jq_select_keys --description 'Selects given keys from json input'
  set key_names (for key in $argv[2..-1]; echo "\\\"$key\\\""; end)
  set key_names_joined (string join "," $key_names)
  set jq_args "\"with_entries(select([.key] | inside([$key_names_joined])))\""
  echo "Command: jq -r $jq_args $argv[1]"
  jq -r $jq_args $argv[1]
end

Когда я запускаю jq_select_keys foo.json bar baz qux на своей раковине рыбы, я получаю следующий вывод:

Command: jq -r "with_entries(select([.key] | inside([\"bar\",\"baz\",\"qux\"])))" foo.json
with_entries(select([.key] | inside(["bar","baz","qux"])))

Теперь интересным моментом является то, что я могу скопировать и вставить вывод оператора echo, и он будет работать, как и ожидалось. Но вывод, который я получаю, это просто строка запроса, которую я передал jq.

Я новичок в программировании оболочки, поэтому, возможно, я испортил свои цитаты. Но кроме этого, я понятия не имею, как заставить эту вещь работать!

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Я новичок в программировании оболочки, поэтому, возможно, я испортил свои цитаты.

Вот и все.

Похоже, вы добавили один слой цитат слишком много.

В рыбе (и большинстве других оболочек, включая, например, bash и zsh), кавычки являются специальными для оболочки только при буквальном использовании.

Это означает, что когда переменная содержит "some string", тогда echo $variable напечатает "some string" - это означает, что команда echo получила строку с кавычками (примечание: bash также будет применяться разделение слов здесь, так что на самом деле он получит "some и string", даже если вы можете предположить, что оно заключено в кавычки).

Я бы посоветовал вам пройтись по вашей функции по очереди и просто echo переменные. Тогда представьте, что вы передали эту точную строку jq - это сработает?

например. $key_names бит:

set key_names (for key in $argv[2..-1]; echo "\\\"$key\\\""; end)

Цель этого, очевидно, состоит в том, чтобы экранировать все ключи (которые являются аргументами начиная со второго) для использования с jq. Это означает, что они должны быть указаны один раз . Их не нужно заключать в кавычки во второй раз, потому что оболочка не заботится о не буквальных кавычках.

Так что любая клавиша должна выглядеть как "key".

Но когда мы ставим

printf '%s\n' $key_names

(который будет печатать каждую клавишу в отдельной строке)

после этого мы видим

\"bar\"
\"baz\"
\"qux\"

Это два слоя цитирования! Сами кавычки и обратные слэши, ускользающие от них.

Итак, давайте удалим один:

set key_names (для ключа в $ argv [2 ..- 1]; echo "\" $ key \ ""; end)

Это приведет к "bar", "baz" и "qux".

(Это можно упростить до set key_names \"$argv[2..-1]\", используя декартово произведение рыбы )

Теперь для следующего бита:

set jq_args "\"with_entries(select([.key] | inside([$key_names_joined])))\""

Ваше намерение здесь состоит в том, чтобы выполнить это, как если бы вы имели

jq -r "with_entries(select([.key] | inside([\"bar\",\"baz\",\"qux\"])))" foo.json

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

Так что просто удалите экранированные кавычки

set jq_args "with_entries(select([.key] | inside([$key_names_joined])))"

и ваша функция должна работать.

Результат:

function jq_select_keys --description 'Selects given keys from json input'
    set key_names \"$argv[2..-1]\"
    set key_names_joined (string join "," $key_names)
    set jq_args "with_entries(select([.key] | inside([$key_names_joined])))"
    echo "Command: jq -r $jq_args $argv[1]"
    jq -r $jq_args $argv[1]
end
0 голосов
/ 28 июня 2018

JQ - забавный зверь. Я не знаю, почему ваш скрипт просто выводит содержимое $ jq_args.

Имеется опция --slurpfile, которая считывает файл строк JSON в переменную массива. Это избавляет вас от динамического построения как массива JSON, так и тела скрипта jq

$ function jqs
        jq -r --slurpfile keys (printf '"%s"\n' $argv[2..-1] | psub) \
            'with_entries(select([.key] | inside($keys)))' $argv[1]
end    

$ cat file.json
{"foo":"a","bar":"b","baz":"c"}

$ jqs file.json foo baz
{
  "foo": "a",
  "baz": "c"
}
...