Detect invalid JSON files in directory tree

I want to fail a build if the directory tree contains a JSON file which is invalid according to the jsonlint package from NPM.

I thought this would be as easy as running:

find . -name \*.json | xargs jsonlint -q

I knew I had a badly formed JSON document on the disk, and this doesn't pick it up.

Further investigation shows that the find command works as expected, however the jsonlint executable is only being called once (and the first JSON file is correct.)

Is there a better way to do this, or is there something I need to learn about xargs here?

I'm running Ubuntu 13.10 Server and can install packages if needed.


Solution 1:

It looks like jsonlint cannot deal with multiple files:

$ jsonlint -h

Usage: jsonlint [file] [options]

file     file to parse; otherwise uses stdin

Note that it always says file, never files. When you run a command and pipe its output to xargs as you have done, xargs will simply concatenate all the output of the command and pass it as input to whatever you have told it to execute. For example:

$ printf "foo\nbar\nbaz\n"
foo
bar
baz
$ printf "foo\nbar\nbaz\n" | xargs echo
foo bar baz

That shows you that the command executed by xargs was

echo foo bar baz

In the case of jsonlint, what you want to do is not jsonlint -q foo bar baz but

$ jsonlint -q foo
$ jsonlint -q bar
$ jsonlint -q baz

The easiest way to do this is to use find as suggested by @fede.evol:

$ find . -name \*.json -exec xargs jsonlint -q {} \;

To do it with xargs you need to use the -I flag:

   -I replace-str
          Replace occurrences of replace-str in the initial-arguments with
          names read from standard input.  Also, unquoted  blanks  do  not
          terminate  input  items;  instead  the  separator is the newline
          character.  Implies -x and -L 1.

For example:

$ find . -name \*.json  | xargs -I {} jsonlint -q {}

Or, to deal with strange names (that contain newlines for example) safely:

$ find . -name \*.json -print0  | xargs -0I {} jsonlint -q '{}'

Solution 2:

The current answers to this question are incomplete since the suggested commands will not print the name of the invalid file. Also, jsonlint is one hell of a slow tool.

Here's a much much faster alternative which shouldn't require additional installations on most systems:

find . -name \*.json -exec echo {} \; -exec python -mjson.tool "{}" \; 2>&1 | grep "No JSON" -B 1