Search code examples
jsoncommand-line-argumentsjqrcdotfiles

How to pass parameters in jq to a function defined in the ~/.jq dotfile?


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

Solution

  • 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]