Bash – convert table to ini file using bash arrays

bashtext processing

I have a challenge to take an output file ("inventory.list") in this format:

hostname1.env1.domain   | abc  | environment1
hostname2.env1.domain   | abc  | environment1
hostname3.env2.domain   | abc  | environment2
hostname4.env2.domain   | abc  | environment2
hostname5.env1.domain   | def  | environment1
hostname6.env2.domain   | def  | environment2
(6 rows)

and write it out into another file in a different format:

[abc.environment1]
hostname1.env1.domain
hostname2.env1.domain

[abc.environment2]
hostname3.env2.domain
hostname4.env2.domain

[def.environment1]
hostname5.env1.domain

[def.environment2]
hostname6.env2.domain

abc and def are roles assigned to servers and there can be multiple servers for each role, as well as same-named roles but in different environments. I have to break out each hostname into unique groups of [role.environment], and additionally, completely delete the final line of the file which is a row count (this file is an output from an sql query).

I can read the file, strip out the pipes and whitespaces and assign/output the role/environment groupings, no problem:

#! /bin/bash
while IFS='| ' read -r certname role env; do
  printf '%s\n' "[""$role"".""$env""]"
done < "/tmp/inventory.list"

…which neatly gives me the role/environment group names:

[abc.environment1]
[abc.environment2]
[def.environment1]
[def.environment2]

but I cannot figure out how to print out the hostnames linked to each role/environment group underneath each group name, neither can I work out how to get my script to ignore the last row count line. I'm guessing I have to further assign my role and environment fields (second and third fields) to its own array to then refer to it to grab out the hostnames linked to each unique grouping, but I have no idea how to achieve this. Can anyone advise, please?

Best Answer

Use an associative array to store the certificate names per role and environment.

#! /bin/bash

unset -v envs
declare -A envs

while IFS='| ' read -r certname role env; do
    envs["$role.$env"]+="$certname"$'\n' 
done < /tmp/inventory.list

for e in "${!envs[@]}" ; do
    printf '%s\n' "[$e]" "${envs[$e]}"
done

To sort the sections, you can print the keys, sort them, and then read them back and output the associated values:

for e in "${!envs[@]}" ; do
    printf '%s\n' "$e"
done | sort | while read -r e ; do
    printf '%s\n' "[$e]" "${envs[$e]}"
done
Related Question