I have a script like this which I use to broadcast commands to several instaces of the postgresql database CLI connected to several different servers. I'm using a hard-coded set of process-substitutions.#!/bin/bash# names have been changed to protect the guiltycred=user=dbadmin password=SECRETdomain=example.comtee \   >( psql -X host=db1.$domain dbname=db1 $cred ) \   >( psql -X host=db2.$domain dbname=db2 $cred ) \   >( psql -X host=db3.$domain dbname=db3 $cred ) \   >( psql -X host=xdb1.$domain dbname=xdb1 $cred ) \   > /dev/null    waitwhat I would like to do is use a for loop to build up an array of substitutions and pass that array to tee, something like this:tee ${p[@]} > /dev/nullbut when I use a loop I get every item in $p as /dev/fd/63 tee gives methis error for each.tee: /dev/fd/63: No such file or directoryexample non-working code:p=()for z in db1 db2 db3 xdb1do  p+=( >( psql -X host=$z.$domain dbname=$z $cred ) )donetee ${p[@]} > /dev/nullIs there a way to make this work?
Build a process substitution list for tee
bash;array;process substitution
This is happening because on this line:   p+=( >( psql -X host=$z.$domain dbname=$z $cred ) )...bash considers the line to be a complete command. When doing process substitution, the STDIN of the substituted process is closed when the command completes.There are only 2 ways I can see to do this:eval. Lets not go there.exec. Lets go there instead: p=()for z in db1 db2 db3 xdb1do  exec {fd}> >(psql -X host=$z.$domain dbname=$z $cred)  p+=( $fd )donecd /dev/fd && exec tee ${p[@]} >/dev/nullThe {fd}> syntax causes bash to allocate a new file descriptor, and assign its value to $fd, which we then shove into $p.Now $p is a bunch of file descriptor numbers, which we have to get tee to write into, so we cd to /dev/fd where the file descriptor numbers are actual files, and then invoke the tee.(There are numerous other ways to skin the cat, but this is the first and most straightforward that sprang to mind)