![](/img/trans.png)
[英]How can I filter a block of data out of a big JSON output by value using JQ?
[英]How can I tell if a jq filter successfully pulls data from a JSON data structure?
我想知道给定的过滤器是否成功从JSON数据结构中提取数据。 例如:
###### For the user steve...
% Name=steve
% jq -j --arg Name "$Name" '.[]|select(.user == $Name)|.value' <<<'
[
{"user":"steve", "value":false},
{"user":"tom", "value":true},
{"user":"pat", "value":null},
{"user":"jane", "value":""}
]'
false
% echo $?
0
注意:成功的结果可以包括布尔值, null
,甚至是空字符串。
###### Now for user not in the JSON data...
% Name=mary
% jq -j --arg Name "$Name" '.[]|select(.user == $Name)|.value' <<<'
[
{"user":"steve", "value":false},
{"user":"tom", "value":true},
{"user":"pat", "value":null},
{"user":"jane", "value":""}
]'
% echo $?
0
如果过滤器不从JSON数据结构中提取数据,我需要知道这一点。 我希望过滤器返回非零返回码。
我如何确定选择器是否成功从JSON数据结构中提取数据而无法提取数据?
重要提示:上面的过滤器只是一个示例,解决方案需要适用于任何jq过滤器。
注意:评估环境是Bash 4.2+。
您可以使用jq Manual
的-e / --exit-status
标志来表示
如果最后一个输出值既不是false也不是null,则将jq的退出状态设置为0;如果最后一个输出值为false或null,则将jq的退出状态设置为0;如果没有生成有效结果,则将4的退出状态设置为4。 通常,如果存在任何使用问题或系统错误,jq将退出2;如果存在jq程序编译错误,则退出3;如果运行jq程序,则退出0。
我可以使用基本过滤器演示如下用法,因为您的示例不适用于我。
要成功查询,
dudeOnMac:~$ jq -e '.foo?' <<< '{"foo": 42, "bar": "less interesting data"}'
42
dudeOnMac:~$ echo $?
0
对于无效查询,使用不存在的实体zoo
,
dudeOnMac:~$ jq -e '.zoo?' <<< '{"foo": 42, "bar": "less interesting data"}'
null
dudeOnMac:~$ echo $?
1
对于错误场景,返回我通过双引用jq
输入流创建的代码2
。
dudeOnMac:~$ jq -e '.zoo?' <<< "{"foo": 42, "bar": "less interesting data"}"
jq: error: Could not open file interesting: No such file or directory
jq: error: Could not open file data}: No such file or directory
dudeOnMac:~$ echo $?
2
我找到了满足我所有要求的解决方案! 请让我知道你在想什么!
这个想法是使用jq -e "$Filter"
作为首次检查。 然后对于返回码1,执行jq "path($Filter)"
检查。 事实上,如果存在进入JSON数据的路径,后者才会成功。
Select.sh
#!/bin/bash
Select()
{
local Name="$1"
local Filter="$2"
local Input="$3"
local Result Status
Result="$(jq -e --arg Name "$Name" "$Filter" <<<"$Input")"
Status=$?
case $Status in
1) jq --arg Name "$Name" "path($Filter)" <<<"$Input" >/dev/null 2>&1
Status=$?
;;
*) ;;
esac
[[ $Status -eq 0 ]] || Result="***ERROR***"
echo "$Status $Result"
}
Filter='.[]|select(.user == $Name)|.value'
Input='[
{"user":"steve", "value":false},
{"user":"tom", "value":true},
{"user":"pat", "value":null},
{"user":"jane", "value":""}
]'
Select steve "$Filter" "$Input"
Select tom "$Filter" "$Input"
Select pat "$Filter" "$Input"
Select jane "$Filter" "$Input"
Select mary "$Filter" "$Input"
并执行上述:
% ./Select.sh
0 false
0 true
0 null
0 ""
4 ***ERROR***
鉴于jq是它的方式,特别是它是面向流的,我倾向于认为更好的方法是定义和使用一个或多个过滤器来实现你想要的区别。 因此,不是编写.a
来访问字段的值,而是编写get("a")
假设get / 1定义如下:
def get(f): if has(f) then .[f] else error("\(type) is not defined at \(f)") end;
现在,您可以轻松判断对象是否有密钥,并且您已准备就绪。 get
这个定义也可以用于数组。
我在下面添加了更新的解决方案
这里的基本问题是,当尝试使用.key
或.[key]
语法从对象检索值时, jq
(按定义 )无法区分缺失键和值为null
的键。
您可以改为定义自己的查找功能:
def lookup(k):if has(k) then .[k] else error("invalid key") end;
然后像这样使用它:
$ jq 'lookup("a")' <<<'{}' ; echo $?
jq: error (at <stdin>:1): invalid key
5
$ jq 'lookup("a")' <<<'{"a":null}' ; echo $?
null
0
如果你然后一致地使用lookup
而不是内置方法,我认为这会给你你想要的行为。
这是另一种方式,减少bash
和更多jq
。
#!/bin/bash
lib='def value(f):((f|tojson)//error("no such value"))|fromjson;'
users=( steve tom pat jane mary )
Select () {
local name=$1 filter=$2 input=$3
local -i status=0
result=$( jq --arg name "$name" "${lib}value(${filter})" <<<$input 2>/dev/null )
status=$?
(( status )) && result="***ERROR***"
printf '%s\t%d %s\n' "$name" $status "$result"
}
filter='.[]|select(.user == $name)|.value'
input='[{"user":"steve","value":false},
{"user":"tom","value":true},
{"user":"pat","value":null},
{"user":"jane","value":""}]'
for name in "${users[@]}"
do
Select "$name" "$filter" "$input"
done
这会产生输出:
steve 0 false
tom 0 true
pat 0 null
jane 0 ""
mary 5 ***ERROR***
这利用了以下事实:过滤器的输入缺少就像empty
一样,而空将触发//
的替代,但是字符串 - 如"null"
或"false"
- 不会。
应该注意, value/1
不适用于对象/数组上的简单键/索引查找的过滤器,但您的解决方案也不会。 我有理由相信,要涵盖所有情况,你需要像这样(或你的) 以及像get
或lookup
这样的东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.