1

bash 4.4.20 and jq-1.6 on RHEL8

I get this nice output from jq and column. Hard to read, though.

pgbackrest info --output=json \
    | jq -r  '.[] | .backup[] | "\(.type) \(.label) \(.info.delta)"' \
    | column -t
full  20250712-224418F                   57448997604
full  20250712-231256F                   57391883702
full  20250713-000402F                   57448997604
full  20250713-013005F                   57447727844
incr  20250713-013005F_20250714-000002I  4960846146
[snip]
incr  20250817-101503F_20250822-000002I  8954487106
incr  20250817-101503F_20250823-000002I  9482330434
full  20250824-014502F                   53490962042
incr  20250824-014502F_20250825-000002I  5594054978
incr  20250824-014502F_20250826-000002I  6828647855

Piping it through a while loop, reading into three separate variables and then printf to convert the third column using numfmt also works, but seems so brutal. Is there a cleverer way to do it (where "it" either converts to human-readable or adds thousands-separators)?

pgbackrest info --output=json \
    | jq -r  '.[] | .backup[] | "\(.type) \(.label) \(.info.delta)"' \
    | (while IFS= read -r line; do 
           IFS=" " read Type Label Size <<< "$line"
           printf "%s %s %s\n" $Type $Label $(numfmt --to=si --suffix=B $Size)
       done) | column -t
full  20250712-224418F                   58GB
full  20250712-231256F                   58GB
full  20250713-000402F                   58GB
full  20250713-013005F                   58GB
incr  20250713-013005F_20250714-000002I  5.0GB
[snip]
incr  20250817-101503F_20250822-000002I  9.0GB
incr  20250817-101503F_20250823-000002I  9.5GB
full  20250824-014502F                   54GB
incr  20250824-014502F_20250825-000002I  5.6GB
incr  20250824-014502F_20250826-000002I  6.9GB
2
  • Can you give us some json input to play with? Commented Aug 27 at 8:25
  • @jubilatious1 markp-fuso's answers copied the text output and processed that. Commented Aug 27 at 12:22

2 Answers 2

3

Using the following to emulate OP's jq output in my environment:

$ cat jq.out
full  20250712-224418F                   57448997604
full  20250712-231256F                   57391883702
full  20250713-000402F                   57448997604
full  20250713-013005F                   57447727844
incr  20250713-013005F_20250714-000002I  4960846146
incr  20250817-101503F_20250822-000002I  8954487106
incr  20250817-101503F_20250823-000002I  9482330434
full  20250824-014502F                   53490962042
incr  20250824-014502F_20250825-000002I  5594054978
incr  20250824-014502F_20250826-000002I  6828647855

Using printf to replace column and add thousands separators:

cat jq.out |
while read -r type label size
do
    printf "%-6s %-35s %'15.0f\n" $type $label $size
done

If needing to type this regularly, and assuming using the same output format each time, we can wrap the guts in a function:

my_column() { 
    while read -r type label size
    do   
        printf "%-6s %-35s %'15.0f\n" $type $label $size
    done  
}

And then the main call becomes:

cat jq.out | my_column

Both of these generate:

full   20250712-224418F                     57,448,997,604
full   20250712-231256F                     57,391,883,702
full   20250713-000402F                     57,448,997,604
full   20250713-013005F                     57,447,727,844
incr   20250713-013005F_20250714-000002I     4,960,846,146
incr   20250817-101503F_20250822-000002I     8,954,487,106
incr   20250817-101503F_20250823-000002I     9,482,330,434
full   20250824-014502F                     53,490,962,042
incr   20250824-014502F_20250825-000002I     5,594,054,978
incr   20250824-014502F_20250826-000002I     6,828,647,855

NOTES:

  • if this does not print the thousands separator then you likely need to play with your locale settings (eg, LC_ALL= printf ... or LC_ALL=en_US.TUF-8 printf ...)
  • adjust the column widths by modifying the -6s, -35s and 15.0f formats
  • the use of the (bash) printf builtin should mean this while/read loop is relatively fast and for smallish sets of data should be on par with the cost of spawning an additional subshell for a column call; for larger datasets the looping/formatting construct could be replaced with a comparable awk (perl, python, etc) call for better performance

OP's code would then become:

pgbackrest info --output=json |
    | jq -r  '.[] | .backup[] | "\(.type) \(.label) \(.info.delta)"' \
    | while read -r type label size; do printf "%-6s %-35s %'15.0f\n" $type $label $size; done

########
# or

pgbackrest info --output=json |
    | jq -r  '.[] | .backup[] | "\(.type) \(.label) \(.info.delta)"' \
    | my_column

In this case since custom formatting is only required for the one column we could fallback to OP's original numfmt idea while eliminating a) the while/read loop, b) the subshell for calling numfmt and c) the additional subshell invoked due to the parens; a couple ideas:

$ cat jq.out | numfmt --field 3 --grouping         | column -t -R 3
$ cat jq.out | numfmt --field 3 --format "%'15.0f" | column -t -R 3

NOTE: depends on locale

Wrapped in a function:

$ my_column() { numfmt --field 3 --grouping | column -t -R 3; }
$ cat jq.out | my_column

These also generate:

full  20250712-224418F                   57,448,997,604
full  20250712-231256F                   57,391,883,702
full  20250713-000402F                   57,448,997,604
full  20250713-013005F                   57,447,727,844
incr  20250713-013005F_20250714-000002I   4,960,846,146
incr  20250817-101503F_20250822-000002I   8,954,487,106
incr  20250817-101503F_20250823-000002I   9,482,330,434
full  20250824-014502F                   53,490,962,042
incr  20250824-014502F_20250825-000002I   5,594,054,978
incr  20250824-014502F_20250826-000002I   6,828,647,855
1
  • If nothing else, inline read -r type label size is useful. Commented Aug 26 at 22:19
2

You could tell numfmt which field to format:

$ numfmt --help
[...]
  -d, --delimiter=X    use X instead of whitespace for field delimiter
      --field=FIELDS   replace the numbers in these input fields (default=1)
                         see FIELDS below
[...]

So, maybe something like:

pgbackrest info --output=json |
    jq -r  '.[] | .backup[] | "\(.type) \(.label) \(.info.delta)"' |
    numfmt --field=3 --to=si --suffix=B |
    column -t
1
  • +1. my first thought on seeing the Q was "why would anyone use a shell while-read loop when you can just tell numfmt which field(s) to format, and do that before the pipe to column -t?" and was about to post an answer like this when i saw your answer. As always, if a shell while read loop is part of an answer, it's almost certainly wrong. And almost certainly the wrong question is being asked. Commented Aug 27 at 3:45

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.