Format data as a table in bash

3

1

I have an array of JSON data being printed to the terminal (OS X) and I want the properties of that to be displayed in a table in the terminal.

Sample query:

aws ec2 describe-instances 
  | jq '[ .[] | .[] | .Instances[] as $ins
  | { groups: $ins.SecurityGroups[].GroupName,
    addresses: [ $ins.PrivateIpAddress, $ins.PublicIpAddress ],
    dns: $ins.PrivateDnsName,
    name: ($ins.Tags[] as $ts | $ts.Key == "Name" | $ts.Value ) }
  | select(.name | contains("prod")) ]'

Stated another way: I want to take the resulting data structure (array of object that contains properties 'addresses', 'groups', 'dns', 'name') and shove each object into a line of a table, inside the terminal/bash.

I don't mind to let the data be EOF-ed before the table starts drawing.

Example JSON:

[
  {
    "name": "prod-clusterX-01",
    "dns": "ip-10-34-XX-XX.eu-west-1.compute.internal",
    "addresses": [
      "10.34.XX.XX",
      "54.246.XX.XX"
    ],
    "groups": "prod-clusterX"
  },
  {
    "name": "prod-revproxy-a",
    "dns": "ip-10-0-XX-XX.eu-west-1.compute.internal",
    "addresses": [
      "10.0.XX.XX",
      "54.229.XX.XX"
    ],
    "groups": "prod-revproxy"
  }
]

Henrik

Posted 2013-08-26T09:42:16.560

Reputation: 181

1can we see an example of the JSON? – simon – 2013-08-26T10:30:31.947

are you open to solutions that depend upon, e.g. python, or third-party tools (at minimum sed, awk etc.)? – simon – 2013-08-26T11:49:07.550

Answers

1

Since OS X comes with Ruby:

sudo gem install json
sudo gem install table_print

For demo purposes, I've saved your JSON string to the file input, but you can pipe it to ruby as well:

ruby -e 'require "rubygems"; require "json"; require "table_print";
d = JSON.parse(ARGF.read);
d = d.map { |row| row["addresses"] = row["addresses"].join(", "); row }; 
tp.set :max_width, 120;
tp d' < input

This outputs:

NAME             | DNS                                       | GROUPS        | ADDRESSES
--------------------------------------------------------------------------------------------------------
prod-clusterX-01 | ip-10-34-XX-XX.eu-west-1.compute.internal | prod-clusterX | 10.34.XX.XX, 54.246.XX.XX
prod-revproxy-a  | ip-10-0-XX-XX.eu-west-1.compute.internal  | prod-revproxy | 10.0.XX.XX, 54.229.XX.XX

slhck

Posted 2013-08-26T09:42:16.560

Reputation: 182 472

I've found the ordering of the table columns to be non-deterministic with table_print. Could be a bug—not sure. – slhck – 2013-08-26T12:11:34.693

Nice. The reason that it's non-deterministic is probably that a Hash isn't ordered. There's the 'map' gem though, that you can probably replace the hash implementation in 'table_print' with. – Henrik – 2013-08-26T20:28:28.947

1

Nice answer @slhck!

Here's some other examples:

aws ec2 describe-instances | jq '[ .[] | .[] | .Instances[] as $ins
  | select(.Platform == null)
  | { instance_type: $ins.InstanceType,
      name: ($ins.Tags[] as $ts | select($ts.Key == "Name") | $ts.Value ),
      security_groups: $ins.SecurityGroups[].GroupName,
      launched: $ins.LaunchTime } ]' | ./to-table.rb

to-table.rb will check for arrays itself now:

#!/usr/bin/env ruby

require "json"
require "table_print"

def join_arrays row
  row.keys.each do |key|
    row[key] = row[key].join(', ') if row[key].respond_to? :join
  end
end

d = JSON.parse(ARGF.read).each { |row| join_arrays row }
w = `/usr/bin/env tput cols`.to_i
tp.set :max_width, w
tp d

(PS: tput cols yields the number of columns that the current bash has.)

Henrik

Posted 2013-08-26T09:42:16.560

Reputation: 181

Good addition. So it turns out you know Ruby anyway :) (Sometimes OS X users are surprised by what they can do in their terminal with pre-installed tools.) – slhck – 2013-08-26T20:36:49.003

Thanks =). Yes, knowing a bit of programming is very helpful. This week we're getting new Macs and we'll be installing Boxen on them and share some development environments across the team! =) – Henrik – 2013-08-26T20:42:02.123