0

I want to read the output of a psql query produced with --field-separator-zero into an array inside my bash script. The best I have tried was the following:

psql -w  -t --quiet --no-align --field-separator-zero -c $'select nickname,first_name,last_name,email from users' | while IFS= read -d '' -a USERS; do 
     echo ${USERS[0]} ${USERS[1]} ${USERS[2]} ${USERS[3]}; 
done;

The above would return each field of a row as a new array. Changing the delimiter to anything else would make the process work, but the problem is the nickname field might contain any character, so I'm forced to use the safe NUL char as a delimiter. Is there any way to do this ?

1 Answer 1

2

I'm assuming here that nickname is a unique key; make the appropriate modifications if a different field should be used in that role.

The below code reads the data into a series of associative arrays, and emits each row in turn.

Note that associative arrays are a Bash 4 feature; if you're on Mac OS, which ships 3.2, use MacPorts or a similar tool to install a modern release.

declare -A first_names=( ) last_names=( ) emails=( )
while IFS= read -r -d '' nickname && \
      IFS= read -r -d '' first_name && \
      IFS= read -r -d '' last_name && \
      IFS= read -r -d '' email; do
  first_names[$nickname]=$first_name
  last_names[$nickname]=$last_name
  emails[$nickname]=$email
done < <(psql ...)

echo "Found users: "
for nickname in "${!emails[@]}"; do
  printf 'nickname - %q\n' "$nickname"
  printf 'email - %q\n' "${emails[$nickname]}"
  printf 'first name - %q\n' "${first_names[$nickname]}"
  printf 'last name - %q\n' "${last_names[$nickname]}"
  echo
done

This technique is described in BashFAQ #1 -- search for -print0 to find its mention.

Sign up to request clarification or add additional context in comments.

3 Comments

Really complicated related to what I expected, but I think there's no better way due to bash's limitations
Doesn't look complicated to me, but then, I suppose that's largely a function of familiarity. If there's a particular thing you'd like to avoid (such as the use of associative arrays), let me know.
@Iman, ...really, the core point here is IFS= read -r -d ''. Granted, 2/3rds of that are clearing unfortunate defaults mandated by standards compliance (without -r, read is defined by POSIX to expand backslash-escape sequences rather than returning data literally; without clearing IFS, trailing whitespace is removed from fields after they're read, again per POSIX). The -d '' is what tells it that the delimiter is NUL -- which works because the shell uses C strings; dereference the first character of an empty string, and you get a NUL.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.