Estou tentando exportar dados de um banco de dados postgres para um arquivo no bash. Mas eu gostaria de ter certeza de que o arquivo só será sobrescrito se a conexão com o banco de dados não falhar (ou seja, eu receber alguns dados de volta)
Tentei usar ofalha no tuboopção, no entanto, se o primeiro comando falhar com um erro (host não existe, por exemplo), o comando cat ainda será executado e gerará um arquivo vazio (eliminando o último conteúdo válido que eu gostaria de evitar). No exemplo abaixo, myhost é um host inválido, então o comando psql simplesmente falharia.
Portanto, a questão maior é como garantir que, quando o pipefail estiver definido, os comandos subsequentes não sejam executados quando o primeiro comando falhar.
#!/bin/sh
set -o nounset
set -o errexit
set -o pipefail
PG_HOST=myhost
psql $PG_HOST -At -F$'\t' -c "SELECT * FROM mytable" | cat > /tmp/mytable.txt
Responder1
set -o pipefail -o errexit
impede que comandos subsequentes sejam executados, mas isso não ajuda, porque você não está tentando impedir umsubseqüentecomando seja executado. Em um pipeline producer | consumer
, os comandos producer
e são executadosconsumer
em paralelo. Você não pode impedir consumer
a partida se producer
falhar porque, salvo um acidente estranho de cronometragem, ela já começou.
Se as duas únicas possibilidades forem “ consumer
sucesso e produção de saída não vazia” e “ consumer
falha e não produção de saída”, você pode usarifne
de moreutils de Joey Hess.
producer | ifne consumer
Porém, não acho que isso funcione no seu caso de uso - pode acontecer de não haver linhas correspondentes (falso negativo e você obter dados obsoletos), a conexão com o banco de dados pode ser perdida no meio (falso positivo e você obter dados truncados ).
Se você precisa saber se o produtor teve sucesso, então você precisa esperar até terminar antes de iniciar o consumidor. E como o consumidor ainda não existe, algo precisa armazenar a produção.
Se a saída não contiver bytes nulos, terminar em um e apenas um caractere de nova linha e não for muito grande, você poderá armazená-la em uma variável shell.
output=$(producer); producer_status=$?
if [ "$producer_status" -ne 0 ]; then
echo >&2 "Producer failed with status $producer_status"
exit "$producer_status"
fi
printf '%s\n' "$output" | consumer
No zsh e em alguns outros shells, incluindo ksh93 e bash, a última linha pode ser simplificada para consumer <<<"$output"
.
Observe que a substituição do comando tira as novas linhas finais. Se as linhas vazias finais forem relevantes, uma solução alternativa é alterar a primeira linha para
output=$(producer; ret=$?; echo .; exit "$?")
producer_status=$? output=${output%?}
$output
conterá então a saída completa, incluindo os caracteres de nova linha à direita, se houver. Em seguida, use printf %s "$output"
em vez de printf '%s\n' "$output"
para alimentá-lo ao arquivo consumer
.
Se a saída for potencialmente muito grande ou puder conter bytes nulos, armazene-a em um arquivo temporário.
Responder2
Como disse DopeGhoti,pipefail
... significa apenas que o erro em qualquer ponto de uma cadeia de tubos será preservado para o código de saída[de um gasoduto].
Para fazer com que o script seja encerrado em caso de erro, use set -e
.
Para evitar a criação do arquivo, crie um temporário e renomeie-o com sucesso, a saber:
set -e
psql $PG_HOST -At -F$'\t' -c \
"SELECT * FROM mytable" > /tmp/mytable.txt~
# ^^^ cf. Useless Use of Cat
mv /tmp/mytable.txt~ /tmp/mytable.txt
Eu sempre usofazerpara esse tipo de coisa, porque ele para em caso de erro e me permite criar pipelines reinicializáveis.