Awesome
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://i.imgur.com/QEmYDw4.png"> <img alt="jqfmt logo" src="https://i.imgur.com/oQTnVNb.png" width="300px"> </picture> </p>Description
I'm frequently passed long shell one-liners that require some visual inspection before running. These days, there's about as much jq in that one-liner as there is bash. I wrote jqfmt to help add line breaks in sensible locations while reading (or writing!) jq.
<p align="center"> <img alt="ray" src="https://i.imgur.com/sgII1ce.png" width="400px"> </p>At time of initial development, I naturally turned to https://github.com/itchyny/gojq expecting to be able to generate and walk a syntax tree—but gojq didn't seem to provide an AST that could be "walked," and it doesn't export its parsing logic to be used in library form. So, I yanked the relevant code out of https://github.com/itchyny/gojq/blob/main/query.go and started from there.
Side note: Ever tried Googling for "jq formatter"? Reading search results is a nightmare since jq itself is, among other things, a formatter.
Getting started
Install
go install -v github.com/noperator/jqfmt/cmd/jqfmt@latest
Usage
𝄢 jqfmt -h
Usage of jqfmt:
-ar
arrays
-f string
file
-o one line
-ob
objects
-op string
operators
-v verbose
Let's take this line of jq…
𝄢 echo '{one: .two, three: [.four, .five, [.fivetwo, .fivethree]], six: map(select((.seven | .eight | .nine)))}' |
jqfmt
{ one: .two, three: [.four, .five, [.fivetwo, .fivethree]], six: map(select((.seven | .eight | .nine))) }
…and format objects.
𝄢 echo '{one: .two, three: [.four, .five, [.fivetwo, .fivethree]], six: map(select((.seven | .eight | .nine)))}' |
jqfmt -ob
{
one: .two,
three: [.four, .five, [.fivetwo, .fivethree]],
six: map(select((.seven | .eight | .nine)))
}
Nice! Let's also do arrays.
𝄢 echo '{one: .two, three: [.four, .five, [.fivetwo, .fivethree]], six: map(select((.seven | .eight | .nine)))}' |
jqfmt -ob -ar
{
one: .two,
three: [
.four,
.five,
[
.fivetwo,
.fivethree
]
],
six: map(select((.seven | .eight | .nine)))
}
It'll read easier if we also break on pipes.
𝄢 echo '{one: .two, three: [.four, .five, [.fivetwo, .fivethree]], six: map(select((.seven | .eight | .nine)))}' |
jqfmt -ob -ar -op pipe
{
one: .two,
three: [
.four,
.five,
[
.fivetwo,
.fivethree
]
],
six: map(select((.seven |
.eight |
.nine)))
}
<details><summary>Full list of valid operators</summary>
<p>
pipe
comma
add
sub
mul
div
mod
eq
ne
gt
lt
ge
le
and
or
alt
assign
modify
updateAdd
updateSub
updateMul
updateDiv
updateMod
updateAlt
</p>
</details>
Back matter
Acknowledgements
- @zjzeit for helping me believe that formatting jq is a reasonable thing to do.
- @colindean for helping me believe that jqfmt was a reasonable thing to build.
- @tracertea for writing jq lines long enough to warrant a formatter.
- @addyosmani for "First do it, then do it right, then do it better." I wrote this on a plane (as the best tools are) over a year ago, but always had more features I wanted to add before releasing it. Doing it "right" might mean a refactor or clean-up; "better" would probably mean incorporating an AST.
See also
- https://news.ycombinator.com/item?id=9448128
- https://github.com/stedolan/jq/issues/2366#issue-1045236954
- https://github.com/itchyny/gojq/issues/62
- https://github.com/wader/jqjq/issues/8
To-do
- handle func definitions
- quickly format jq by appending
fmt
tojq
on the CLI
License
This project is licensed under the MIT License.