如何编写委托给 jq 的 fish 函数?
How to write a fish function that delegates to jq?
我正在尝试编写一个名为 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
我的鱼 shell 时,我得到以下输出:
Command: jq -r "with_entries(select([.key] | inside([\"bar\",\"baz\",\"qux\"])))" foo.json
with_entries(select([.key] | inside(["bar","baz","qux"])))
现在,有趣的一点是我可以复制粘贴 echo 语句的输出,它 运行 符合预期。但是我得到的输出只是我传递给 jq 的查询字符串。
我是 shell 编程的新手,所以我可能搞砸了我的报价。但除此之外,我不知道如何让这个东西工作!
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"
}
I am new to shell programming, so I might have messed up my quotes.
基本上就是这样。
看来你加了一层引号太多了。
在 fish(以及大多数其他 shell 中,包括例如 bash 和 zsh),引号仅在按字面使用时对 shell 有特殊意义。
这意味着当变量包含 "some string"
时,那么 echo $variable
将打印 "some string"
- 这意味着 echo
命令接收到字符串 with引号 (注意:bash 也会在此处应用分词,因此它实际上会收到 "some
和 string"
,即使您可能认为它被引用)。
我建议你一个一个地检查你的函数,然后简单地 echo
变量。然后假设您将 那个确切的字符串 传递给了 jq
- 它会起作用吗?
例如$key_names
位:
set key_names (for key in $argv[2..-1]; echo "\\"$key\\""; end)
这样做的目的显然是转义所有键(从第二个开始的参数)以供jq
使用。这意味着它们需要被引用一次。它们不需要被第二次引用,因为 shell 不关心非文字引号。
所以任何键都应该看起来像 "key"
。
但是当我们把
printf '%s\n' $key_names
(这将在其自己的行上打印每个键)
在此之后,我们看到
\"bar\"
\"baz\"
\"qux\"
那是 两 层引用!引号本身,以及转义它们的反斜杠。
所以让我们删除一个:
set key_names (for key in $argv[2..-1]; echo "\"$key\""; end)
这将导致 "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
我正在尝试编写一个名为 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
我的鱼 shell 时,我得到以下输出:
Command: jq -r "with_entries(select([.key] | inside([\"bar\",\"baz\",\"qux\"])))" foo.json
with_entries(select([.key] | inside(["bar","baz","qux"])))
现在,有趣的一点是我可以复制粘贴 echo 语句的输出,它 运行 符合预期。但是我得到的输出只是我传递给 jq 的查询字符串。
我是 shell 编程的新手,所以我可能搞砸了我的报价。但除此之外,我不知道如何让这个东西工作!
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"
}
I am new to shell programming, so I might have messed up my quotes.
基本上就是这样。
看来你加了一层引号太多了。
在 fish(以及大多数其他 shell 中,包括例如 bash 和 zsh),引号仅在按字面使用时对 shell 有特殊意义。
这意味着当变量包含 "some string"
时,那么 echo $variable
将打印 "some string"
- 这意味着 echo
命令接收到字符串 with引号 (注意:bash 也会在此处应用分词,因此它实际上会收到 "some
和 string"
,即使您可能认为它被引用)。
我建议你一个一个地检查你的函数,然后简单地 echo
变量。然后假设您将 那个确切的字符串 传递给了 jq
- 它会起作用吗?
例如$key_names
位:
set key_names (for key in $argv[2..-1]; echo "\\"$key\\""; end)
这样做的目的显然是转义所有键(从第二个开始的参数)以供jq
使用。这意味着它们需要被引用一次。它们不需要被第二次引用,因为 shell 不关心非文字引号。
所以任何键都应该看起来像 "key"
。
但是当我们把
printf '%s\n' $key_names
(这将在其自己的行上打印每个键)
在此之后,我们看到
\"bar\"
\"baz\"
\"qux\"
那是 两 层引用!引号本身,以及转义它们的反斜杠。
所以让我们删除一个:
set key_names (for key in $argv[2..-1]; echo "\"$key\""; end)
这将导致 "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