2

Goal

I'm trying to declare functions in my ~/.jq so they can be re-used. I am not sure I've got the correct syntax for passing parameters. I've looked at the builtin.jq source but the recursion flew over my head. Can anyone help clarify if the way I've done it below is correct?

N.B. I know it's more efficient to run jq <json.txt '...' instead of the "useless use of cat", but for clarity I've written it that way so all the jq syntax is on the RHS.

Source JSON (what I used as a test):

curl -s 'https://packagecontrol.io/channel_v3.json' >json.txt

Example without using custom function

This is jq code executed on the commandline:

$ cat json.txt |
jq -c --arg q "colorhelper" 'paths as $p |
  select(getpath($p)?|test($q;"i")) |
  $p'

==> ["packages_cache","https://packagecontrol.io/repository.json",625,"name"]...

Function in ~/.jq

I defined a function named grep in my ~/.jq:

def grep($q; $f):
  paths as $p |
  select(getpath($p)?|test($q;$f)) |
  $p ;

...and call it like this:

$ cat json.txt | jq -c 'grep("colorhelper";"i")'
==> ["packages_cache","https://packagecontrol.io/repository.json",625,"name"]...

"It works", but I'd like to know how to declare the function such that if the 2nd parameter is omitted ("i" in this case), it will still succeed. As-is, an error is thrown if the function is called with only a single parameter (jq: error: grep/1 is not defined at <top-level>)

Platform

  • macOS 11.2.1
  • jq 1.6 (Homebrew)
$ jq --version
jq-1.6
peak
  • 105,803
  • 17
  • 152
  • 177
luckman212
  • 473
  • 1
  • 7
  • 15

1 Answers1

2

All you have to do is add a suitable def for grep/1 in ~/.jq. For example, you could add (after grep/2):

def grep($q): grep($q; "i");

if you want "i" to be the default. (You could change the default to be "" if you like, but then your query will not return any answers with json.txt.)

Example

With ~/.jq as above:

jq -c --arg q "colorhelper" 'grep($q)' json.txt
["packages_cache","https://packagecontrol.io/repository.json",625,"name"]
["packages_cache","https://packagecontrol.io/repository.json",625,"homepage"]
["packages_cache","https://packagecontrol.io/repository.json",625,"readme"]
["packages_cache","https://packagecontrol.io/repository.json",625,"issues"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",0,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",1,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",2,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",3,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",4,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",5,"url"]
["packages_cache","https://packagecontrol.io/repository.json",625,"releases",6,"url"]

If you also defined grep/0 as def grep: grep($q); then your invocation would be a little less redundant:

jq -c grep --arg q colorhelper json.txt

N.B.

Having ~/.jq as a file is fine but a bit antiquated: recent versions of jq have a module system which tends to assume ~/.jq is a directory.

So if your grep/1 definition was in ~/.jq/lib.jq, you would find:

echo '["abc"]' | jq -c 'include "lib"; grep("a")'
[0]

and similarly:

echo '["abc"]' | jq -c 'import "lib" as lib; lib::grep("a")'
[0]
peak
  • 105,803
  • 17
  • 152
  • 177
  • Good info about the module system. – luckman212 Feb 24 '21 at 16:28
  • what's the difference between import and include ? – Stephan Nov 21 '21 at 17:01
  • `include` imports a module as if it were included in place(no namespace e.g. $NAME::NAME which happens on `import`). The module's symbols are imported into the caller's namespace as if the module's content had been included directly. – eephillip Jan 18 '22 at 16:44