Some notes (to myself) about formatting text in
These days I'm having to deal with a steadily increasing number of
commands that either output JSON only or where JSON is their best
output option, and I want to reformat some of that JSON to a more
useful or more readable text-based format. The obvious tool to do
this with is
jq, at least for
simple reformatting (I think there's some things that are too
tangled for jq). However, every
time I need to do this, I keep having to look up how to format text
jq. Jq has a very big manual and a lot of features, so
here's some notes to my future self about this.
In the normal case I have some fixed fields that I want to present in a line, for example information about SSH login failures:
logcli ... | jq '. | (.value, .metric.rhost, .metric.ruser)'
(I can leave off the '(' and ')' and it works, to my surprise, but I'm banging rocks here.)
First, I basically always want to use '
jq -r' so that strings
aren't quoted (and strings may include what are actually numbers
but rendered as strings by the tool). Then I know of several ways
to produce text in a useful form.
Often the simplest way is to put the values into a JSON array and
run them through the '
@tsv' filter (described in the "Format
strings and escaping" section of the manual), which produces tab
$ ... | jq -r '. | [.value, .metric.ruser, .metric.rhost] | @tsv' 1596 root 184.108.40.206 [...]
By itself, @tsv just puts a tab between things, which can leave me
with ragged columns if the length is different enough. As various
people on the Internet will tell you, the
column program can
be used to align the output nicely.
The next option is string interpolation:
jq -r '. | "\(.value): \(.metric.rhost) -> \(.metric.ruser) on \(.metric.host)"' 1596: 220.127.116.11 -> root on <ourhost> [...]
String interpolation permits embedded "'s, so you can write things
\(.metric.ruser // "<no-such-login>")' even in the interpolation
The third option is string concatenation, provided that all of your
values are strings (or you use
@text on things).
jq -r '. | (.value | @text) + " " + (.metric.ruser // "??") + "@" + .metric.host + " from " + .metric.rhost' 1596 root@<ourhost> from 18.104.22.168
(I got this use of string concatenation from here.)
If I'm doing this text formatting in jq purely for output, I think
it's clear that @tsv is the easiest option and has the simplest
jq expression. I suspect I'd never have a reason to use string
concatenation to produce the entire output line instead of doing
string interpolation. Well, maybe if I'm in some shell context that
jq all of those '
\(' bits too hard, since string
concatenation doesn't need any backslashes.
But honestly, if I need complicated formatting I'm more likely to fix jq's output up in awk with its printf. Awk printf will do a lot that's at least quite annoying in jq.