bash 如何判断 jq 过滤器是否成功从 JSON 数据结构中提取数据?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41124355/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:31:47  来源:igfitidea点击:

How can I tell if a jq filter successfully pulls data from a JSON data structure?

jsonbashjqreturn-code

提问by Steve Amerige

I want to know if a given filter succeeds in pulling data from a JSON data structure. For example:

我想知道给定的过滤器是否成功地从 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

Note:successful results can include boolean values, null, and even the empty string.

注意:成功的结果可以包括布尔值、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

If the filter does not pull data from the JSON data structure, I need to know this. I would prefer the filter to return a non-zero return code.

如果过滤器没有从 JSON 数据结构中提取数据,我需要知道这一点。我希望过滤器返回一个非零的返回码。

How would I go about determining if a selector successfully pulls data from a JSON data structure vs. fails to pull data?

我将如何确定选择器是成功从 JSON 数据结构中提取数据还是未能提取数据?

Important:The above filter is just an example, the solution needs to work for any jq filter.

重要提示:上述过滤器只是一个示例,该解决方案需要适用于任何 jq 过滤器。

Note:the evaluation environment is Bash 4.2+.

注:评估环境为Bash 4.2+。

采纳答案by Steve Amerige

I've found a solution that meets all of my requirements! Please let me know what you think!

我找到了满足我所有要求的解决方案!请让我知道你的想法!

The idea is use jq -e "$Filter"as a first-pass check. Then for the return code of 1, do a jq "path($Filter)"check. The latter will only succeed if, in fact, there is a path into the JSON data.

这个想法是jq -e "$Filter"用作首次通过检查。然后对于返回码1,做一个jq "path($Filter)"检查。后者只有在事实上存在进入 JSON 数据的路径时才会成功。

Select.sh

选择.sh

#!/bin/bash

Select()
{
   local Name=""
   local Filter=""
   local Input=""
   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"

And the execution of the above:

以及上面的执行:

% ./Select.sh
0 false
0 true
0 null
0 ""
4 ***ERROR***

回答by Inian

You can use the -e / --exit-statusflag from the jq Manual, which says

您可以使用 中的-e / --exit-status标志jq Manual,上面写着

Sets the exit status of jq to 0 if the last output values was neither false nor null, 1 if the last output value was either false or null, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran.

如果最后一个输出值既不是假也不是空,则将 jq 的退出状态设置为 0,如果最后一个输出值是假或空,则设置为 1,如果没有产生有效结果,则设置为 4。通常,如果出现任何使用问题或系统错误,jq 以 2 退出,如果存在 jq 程序编译错误,则以 3 退出,如果 jq 程序运行则以 0 退出。

I can demonstrate the usage with a basic filter as below, as your given example is not working for me.

我可以用下面的基本过滤器演示用法,因为您给定的示例对我不起作用。

For a successful query,

对于成功的查询,

dudeOnMac:~$ jq -e '.foo?' <<< '{"foo": 42, "bar": "less interesting data"}'
42
dudeOnMac:~$ echo $?
0

For an invalid query, done with a non-existent entity zoo,

对于使用不存在的实体完成的无效查询zoo

dudeOnMac:~$ jq -e '.zoo?' <<< '{"foo": 42, "bar": "less interesting data"}'
null
dudeOnMac:~$ echo $?
1

For an error scenario, returning code 2which I created by double-quoting the jqinput stream.

对于错误场景,返回2我通过双引号jq输入流创建的代码。

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

回答by peak

Given that jq is the way it is, and in particular that it is stream-oriented, I'm inclined to think that a better approach would be to define and use one or more filters that make the distinctions you want. Thus rather than writing .ato access the value of a field, you'd write get("a")assuming that get/1 is defined as follows:

鉴于 jq 就是这样,特别是它是面向流的,我倾向于认为更好的方法是定义和使用一个或多个过滤器来区分你想要的。因此,不是写入.a访问字段的值,而是get("a")假设 get/1 定义如下:

def get(f): if has(f) then .[f] else error("\(type) is not defined at \(f)") end;

Now you can easily tell whether or not an object has a key, and you're all set to go. This definition of getcan also be used with arrays.

现在您可以轻松判断一个对象是否有键,然后就可以开始了。这个定义get也可以用于数组。

回答by Thedward

I've added an updated solution below

我在下面添加了一个更新的解决方案

The fundamental problem here is that when try to retrieve a value from an object using the .keyor .[key]syntax, jq— by definition— can't distinguish a missing key from a key with a value of null.

这里的基本问题是,当尝试使用.keyor.[key]语法从对象中检索值时,jq根据定义,无法区分丢失的键和值为 的键null

You can instead define your own lookup function:

您可以改为定义自己的查找函数:

def lookup(k):if has(k) then .[k] else error("invalid key") end;

Then use it like so:

然后像这样使用它:

$ jq 'lookup("a")' <<<'{}' ; echo $?
jq: error (at <stdin>:1): invalid key
5

$ jq 'lookup("a")' <<<'{"a":null}' ; echo $?
null
0

If you then use lookupconsistently instead of the builtin method, I think that will give you the behaviour you want.

如果您然后lookup始终使用而不是内置方法,我认为这会给您想要的行为。



Here's another way to go about it, with less bashand more jq.

这里的另一种方式去了解它,用更少的bashjq

#!/bin/bash

lib='def value(f):((f|tojson)//error("no such value"))|fromjson;'

users=( steve tom pat jane mary )

Select () {
  local name= filter= input=
  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

This produces the output:

这会产生输出:

steve   0 false
tom     0 true
pat     0 null
jane    0 ""
mary    5 ***ERROR***

This takes advantage of the fact the absence of input to a filter acts like empty, and empty will trigger the alternative of //, but a string — like "null"or "false"— will not.

这利用了过滤器没有输入的事实,就像 一样empty,空将触发 的替代//,但字符串——像"null""false"——不会。

It should be noted that value/1will not work for filters that are simple key/index lookups on objects/arrays, but neither will your solution. I'm reasonably sure that to cover all the cases, you'd need something like this (or yours) andsomething like getor lookup.

应该注意的是,value/1这不适用于在对象/数组上进行简单键/索引查找的过滤器,但您的解决方案也不会。我有理由相信,要涵盖所有情况,您需要这样的(或您的)类似的东西getlookup