如何编写委托给jq的fish函数?

时间:2018-06-28 10:01:03

标签: shell jq fish

我正在尝试编写一个名为jq_select_keys的fish函数,该函数从给定的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的查询字符串。

我是Shell编程的新手,所以我可能把报价弄乱了。但是除此之外,我不知道如何使这个东西正常工作!

2 个答案:

答案 0 :(得分:5)

  

我是Shell编程的新手,所以我可能把报价弄乱了。

基本上就是这样。

您似乎增加了一层引号。

在鱼(以及大多数其他壳,包括bash和zsh)中,引号仅在按字面使用时对壳而言是特殊的。

这意味着当变量包含"some string"时,echo $variable将显示"some string"-这意味着echo命令收到带有引号的字符串 (注意:bash在这里也会应用分词,因此即使您可能会引用它,它实际上也会收到"somestring")。

我建议您一个接一个地浏览函数,并简单地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\"

那是两层的报价!引号本身,反斜杠转义。

所以我们删除一个:

设置key_names(用于$ argv [2 ..- 1]中的密钥;回显“ \” $ key \“”;结束)

这将导致"bar""baz""qux"

(这可以通过使用fish的cartesian product简化为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

答案 1 :(得分:2)

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"
}