looping through JSON array in shell script
Extracting the members
jq -c '.children.values[]|[.path.components[0],.type,.size]'
.children.values[]
outputs every member of the array.values
.|
pipes the previous result through the next filter, rather like a shell pipe[
...,
...,
...]
makes all the terms inside appear in a single array- The
-c
option produces "compact" format ie. one object per line
Result:
[".gitignore","FILE",224]
["Jenkinsfile","FILE",1396]
["README.md","FILE",237]
...
Formatting the result
If you want to output a neatly-aligned table, that's a task better handled by other tools, such as column
or paste
.
jq -c '.children.values[]|[.path.components[0],.type,.size]' | column -t -s'[],"'
-t
tellscolumn
to guess the number of columns based on the input-s...
specifies the delimiter character(s)
Result:
.gitignore FILE 224
Jenkinsfile FILE 1396
README.md FILE 237
This relies on the characters [
, ]
, ,
and "
not appearing in your filenames, which is not a safe assumption.
paste
can also arrange multiple inputs side-by-side. For this, we can remove the JSON structures altogether, and output raw lines (hat-tip to @muru):
jq -r '.children.values[]|.path.components[0],.type,.size' | paste - - -
paste - - -
means 3 columns, all read from the the same source. This time, the only assumption is that the filenames don't contain newlines.
For the use case provided in the Question, @JigglyNaga's answer is probably better than this, but for some more complicated task, you could also loop through the list items using keys
:
from file
:
for k in $(jq '.children.values | keys | .[]' file); do
...
done
or from string:
for k in $(jq '.children.values | keys | .[]' <<< "$MYJSONSTRING"); do
...
done
So e.g. you might use:
for k in $(jq '.children.values | keys | .[]' file); do
value=$(jq -r ".children.values[$k]" file);
name=$(jq -r '.path.name' <<< "$value");
type=$(jq -r '.type' <<< "$value");
size=$(jq -r '.size' <<< "$value");
printf '%s\t%s\t%s\n' "$name" "$type" "$size";
done | column -t -s$'\t'
if you have no newlines for the values, you can make it with a single jq
call inside the loop which makes it much faster:
for k in $(jq '.children.values | keys | .[]' file); do
IFS=$'\n' read -r -d '' name type size \
<<< "$(jq -r ".children.values[$k] | .path.name,.type,.size" file)"
printf '%s\t%s\t%s\n' "$name" "$type" "$size";
done | column -t -s$'\t'
jq
can render its output into a variety of formats: see https://stedolan.github.io/jq/manual/#Formatstringsandescaping
For tab-separated output:
$ jq -r '.children.values[] | [.path.name, .type, .size] | @tsv' file.json
.gitignore FILE 224
Jenkinsfile FILE 1396
README.md FILE 237
pom.xml FILE 2548
src DIRECTORY