I thought I'd better start a new query for this, since my original query was fully answered (thank you!).

I have two files which are both outputs of a postgresql command. The first (/tmp/inventory.list) was the subject of my original query convert table to ini file using bash arrays, for which I'm using the array command kindly suggested by @choroba. Given that the format of my second file (/tmp/inventory2.list) is very similar to the first, I thought I could adapt the array scripting to process this file similarly, but I'm obviously messing something up.

My input file /tmp/inventory2.list is in the format:

environment1 | hostname1.environment1.domain
environment1 | hostname2.environment1.domain
environment1 | hostname3.environment1.domain
environment2 | hostname4.environment2.domain
environment2 | hostname5.environment2.domain
environment3 | hostname6.environment3.domain

This has to be read in, and written to another file in the grouped format:




There has to be a line space in between the environment groupings, the group names have to be displayed in alphabetical order and the hostnames have to be alphabetically sorted within the groups. There may be many hostnames to each environment group, but each group should only be displayed once.

To complicate things, there is a blank line at the end of the input file, which I cannot get my postgresql query to leave out (-t or --tuples-only removes the row count line usually written at the end, but doesn't remove the final blank line), so that needs to be stripped out and not written to the new output file.

I tried to adapt @choroba's array command to read in this file and output it correctly, but although it works perfectly for my first file, my adaptation doesn't work. I have:

  1 #! /bin/bash
  3 unset -v envs
  4 unset -v hosts
  5 declare -A envs
  6 declare -A hosts
  7 rm -f /tmp/hosts.txt
  9 while IFS='| ' read -r certname role env; do
 10     envs["$role.$env"]+="$certname"$'\n'
 11 done < /tmp/inventory.list
 13 for e in "${!envs[@]}" ; do
 14 #
 15     printf '%s\n' "$e"
 16 done | sort | while read -r e ; do
 17     printf '%s\n' "[$e]" "${envs[$e]}" >> /tmp/hosts.txt
 18 done
 20 while IFS='| ' read -r env certname; do
 21     hosts["$env"]+="$certname"$'\n'
 22 done < /tmp/inventory2.list
 24 for f in "${!hosts[@]}" ; do
 25     printf '%s\n' "$f"
 26 done | sort | while read -r f ; do
 27     printf '%s\n' "[$f]" "${hosts[$f]}" >> /tmp/hosts1.txt
 28 done

Lines 9 to 18 (and associated lines 3 and 5) were the code given me by @choroba and work perfectly on the file read in, /tmp/inventory.list.

Lines 20 to 28 (and associated lines 4 and 6) are my adaptation to deal with this second file, /tmp/inventory2.list. When run, I get the error:

 line 21: hosts["$env"]: bad array subscript

I've tinkered with this for hours, but cannot see what's wrong in my adapted snippet. Anyone any thoughts, please?

  • associative arrays in bash (contrary to most other languages and all shells supporting associative arrays) doesn't support empty keys. – Stéphane Chazelas Jan 13 '16 at 14:14

You mention a blank line in an input file; if it's in /tmp/inventory2.list, then that's the problem.

Work around in several ways; one would be to test the $env variable before trying to use it:

[ -n "$env" ] && hosts["$env"]+="$certname"$'\n'

Another would be to grep for any character in the file before reading it:

grep . inventory2.list | while IFS='| ' read -r env certname; do

(adjust the grep if your "blank" line has spaces; something like grep [a-z])

  • Thanks Jeff, it appears that's exactly what the problem was. I found a way to remove the blank line that postgres adds on to the end of its query output by adding | grep -v "^$" to the end of the postgres query before outputting to my inventory2.list file. Then my array worked ok. – HelenH Jan 13 '16 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.