Why does jq sometimes require a filter expression, and sometimes not?

Viewed 122

I have some unformatted JSON files. I want to format them with jq and see the formatted output via less:

cat *.json | jq | less

But it doesn't work. Just shows the jq's help.

O.K., jq needs a filter, minimum a ., but...

cat *.json | jq

works for me.

What is the difference? Is this a Bash bug, seeing that parsing the command line is the shell's task? Why does this only happen with jq?

1 Answers

jq does some things differently if it detects that its output stream is a terminal. For one thing, it colour-codes its output (unless you explicitly turn that option off). But more relevantly to this question, for version 1.5, if it detects that its output stream is a terminal and its input stream is not a terminal and there are no non-option command-line arguments at all, it assumes that you meant to use the filter ..

So that's what happens when you type cat *.json | jq. In that case, jq's input stream is a pipe (from the cat command) but its output stream is the terminal. So it assumes a default filter of ..

But when you pipe the output of jq through less, jq's output stream is no longer a terminal. Now it's a pipe (connected to a less process). So the automatic filter . is not inserted, and jq complains that no filter was provided.

However, if you upgrade to v1.6, you'll find that the behaviour has changed. In v1.6, an invocation without any filter will be adjusted to . unless both the input stream and the output stream are a terminal. So if you upgrade, you'll find that cat *.json | jq | less works roughly the same way as cat *.json | jq, except that the output is coloured and, of course, not paged.

By the way, if you wanted to see coloured output with less (and you're using Gnu less), then you could use the command:

cat *.json | jq . -C | less -R   # The . is not necessary with v1.6
Related