Construa uma lista de substituição de processo para tee

2

Eu tenho um script como este que uso para transmitir comandos para várias instâncias da CLI do banco de dados postgresql conectados a vários servidores diferentes. Estou usando um conjunto codificado de substituições de processos.

#!/bin/bash
# names have been changed to protect the guilty
cred="user=dbadmin password=SECRET"
domain=example.com

tee \
   >( 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    
wait

O que eu gostaria de fazer é usar um loop for para criar uma matriz de substituições e passar essa matriz para tee, algo assim:

tee "${p[@]}" > /dev/null

mas quando eu uso um loop eu recebo cada item em $p como /dev/fd/63 tee me dá esse erro para cada um.

tee: /dev/fd/63: No such file or directory

exemplo de código não funcional:

p=()
for z in db1 db2 db3 xdb1
do
  p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
done
tee "${p[@]}" > /dev/null

Existe uma maneira de fazer isso funcionar?

    
por Jasen 07.06.2016 / 07:01

2 respostas

2

Isso está acontecendo porque nesta linha:

  p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )

... bash considera a linha como um comando completo. Ao fazer a substituição do processo, o STDIN do processo substituído é fechado quando o comando é concluído.

Existem apenas duas maneiras de ver isso:

  1. %código%. Não vamos lá.
  2. %código%. Vamos lá em vez disso:

p=()
for z in db1 db2 db3 xdb1
do
  exec {fd}> >(psql -X "host=$z.$domain dbname=$z $cred")
  p+=( $fd )
done
cd /dev/fd && exec tee "${p[@]}" >/dev/null

A sintaxe eval faz com que o bash aloque um novo descritor de arquivo e atribua seu valor a exec , que então é enviado para {fd}> .
Agora $fd é um monte de números de descritores de arquivos, que temos que ter $p para escrever, então $p to tee onde os números do descritor de arquivos são arquivos reais e, em seguida, invocar cd .

(Existem inúmeras outras maneiras de esfolar o gato, mas este é o primeiro e mais simples que surgiu na mente)

    
por 07.06.2016 / 08:38
0

Aqui está um exemplo simples do que eu quis dizer no meu comentário.

Um loop for (com repetição até o sucesso de cada comando do psql, executado em um sub-shell em segundo plano). Não há necessidade de uma matriz de descritores de arquivos de substituição de processos gerados.

cred='user=dbadmin password=SECRET'
domain='example.com'

tf=$(mktemp)
cat > "$tf"

for z in db1 db2 db3 xdb1 ; do
  ( while ! psql -X "host=$z.$domain dbname=$z $cred" < "$tf" ; do : ; done) &
done > /dev/null

# don't delete the tempfile until all jobs have completed
wait

rm -f "$tf"

Opcionalmente, use sleep x em vez de : no loop while para ter um pequeno atraso entre cada tentativa de repetição.

Uma versão mais inteligente faria mais dentro do loop while, verifique o status de saída e / ou grep stderr para descobrir a causa de qualquer falha e responder apropriadamente.

    
por 08.06.2016 / 01:39