Numeric argument passed with jq --arg not matching data with ==

点点圈 提交于 2019-11-30 21:39:44
mklement0

jq is data-type-aware:

  • .ID, as defined in the JSON input, is a number,

  • but any command-line argument passed with --arg (such as v here) is invariably a string (whether you quote the value or not),

so, in order to compare them, you must use an explicit type conversion, such as with tonumber/1:

jq --arg v '2' '.jobStatus[] | select(.ID == ($v | tonumber)) | .status' test.txt

Given that you're only passing a scalar argument here, the following solution, using --argjson (jq v1.5+) is a bit of an overkill, but it is an alternative to explicit type conversion in that passing a JSON argument in effect passes typed data:

jq --argjson v '{ "ID": 2 }' '.jobStatus[] | select(.ID == $v.ID) | .status' test.txt

peak's answer demonstrates that even --argjson v 2 works (in which case comparing to $v works directly), which is certainly the most concise solution, but may require an explanation:

  • Even though 2 may not look like JSON, it is: it is a valid JSON text containing a single value of type number (see json.org).

    • Specifically, it is the fact that 2 is an unquoted token that starts with a digit that makes it a number in the context of JSON (the JSON string-value equivalent is "2", which from the shell would have to be passed as '"2"' - note the embedded double quotes).
  • Therefore jq interprets --argjson -v 2 as a number, and comparison .ID == $v works as intended (note that the same applies to --argjson -v '2' / --argjson -v "2", where the shell removes the quotes before jq sees the value).
    By contrast, anything you pass with --arg is always a string value that is used as-is.

  • In other words: --argjson, whose purpose is to accept arbitrary JSON texts as strings (such as '{ "ID": 2 }' in the example above), can also be used to pass number-string scalars to force their interpretation as numbers.
    The same technique also works with Boolean strings true and false.


Tip of the hat to peak for his help.

Assuming you want to check for the JSON value 2, you have a choice to make - either convert the argument of --arg to a number, or use --argjson with a numeric argument. These alternatives are illustrated by the following:

jq --arg v 2 '.jobStatus[] | select(.ID == ($v|tonumber) | .status' 

jq --argjson v 2 '.jobStatus[] | select(.ID == $v) | .status'

Note that --argjson requires a relatively recent version of jq.

Of course, if you want to "normalize" .ID so that it's always treated as a string, you could write:

jq --arg v 2 '.jobStatus[] | select((.ID|tostring) == $v) | .status'
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!