Saindo de um pipeline se um comando anterior falhar

5

Estou tentando verificar o número de tarefas PBS em execução e enfileiradas, analisando a saída de qstat -tn1 de um script bash . Até agora, isso funcionou:

count ()
{
    qstat -tn1 | awk '
        BEGIN { R = 0; Q = 0; }
        $10 == "R" { R++ }
        $10 == "Q" { Q++ }
        END { print R, Q }'
}

if read -r R Q < <(count)
    ...

No entanto, vejo que qstat ocasionalmente falha por motivos desconhecidos. Nesse caso, não imprime nada para stdout e alguma mensagem de erro para stderr e sai com um status diferente de zero (bastante padrão). No entanto, awk não sabe que qstat falhou e, felizmente, imprime 0 0 da entrada vazia recebida. Então, read atribui 0 a ambos R e Q sem saber que qstat realmente falhou.

  1. Preciso inicializar R e Q com 0 no bloco BEGIN do script awk , porque pode não haver processos em execução ou nenhum processo na fila, e preciso imprimir 0 , não apenas uma string vazia, pelo número de tais processos.
  2. Eu poderia fazer set -o pipefail , o que permitiria que count saísse com um status diferente de zero, mas read não pode ver o status de saída e awk será executado e imprimir 0 0 para a entrada vazia de qualquer maneira.
  3. Eu poderia tentar um pipe nomeado e subprocessos, mas ter que gerenciá-los parece um exagero muito complicado.

Existe alguma boa maneira de permitir que o chamador de count detecte sua falha?

    
por musiphil 03.07.2012 / 01:19

2 respostas

4

Acho que a leitura de count em uma substituição de processo impede que você obtenha seu status de retorno. Então não faça isso. Em vez disso, armazene o resultado em uma variável ou use um pipe.

count=$(count)
if [ $? -eq 0 ]; then
  read -r R Q <<<"$count"
  …

ou

set -o pipefail
if count | { read -r R Q; … }

Outra possibilidade é usar a variável PIPESTATUS para verificar o status de retorno do primeiro comando.

count=$(qstat -tn1 | awk …)
if ((${PIPESTATUS[0]} == 0)); then
  read P Q
  …

Como alternativa, providencie para que o awk imprima algo diferente (por exemplo, nada) quando sua entrada estiver vazia.

awk '
    BEGIN { R = 0; Q = 0; }
    $10 == "R" { R++ }
    $10 == "Q" { Q++ }
    END { if (NR) print R, Q }'

Você pode testar se a entrada de um comando está vazia com ifne de moreutils ou outros métodos . Mas já que você está entrando no awk, é melhor fazê-lo dentro do script awk que você já tem.

Se você precisar obter o status de retorno do comando qstat , poderá alimentá-lo para o awk como uma linha de entrada extra. Para facilitar a análise, organize a última linha para ter um formato exclusivo.

{
  qstat -tn1
  echo exit_code = $?
} | awk '
    …
    /^exit_code = / { status = $3 }
    END { if (status == 0) print Q, R }
'
    
por 03.07.2012 / 01:28
2

Isto:

count ()
{
    { qstat -tn1 || echo "EPIC FAIL" } | awk '
        BEGIN { R = 0; Q = 0; }
        $10 == "R" { R++ }
        $10 == "Q" { Q++ }
        END { print R, Q }'
}

if read -r R Q < <(count)
    ...

Se qstat for bem-sucedida, sua saída será enviada para awk . Se qstat retornar status diferente de zero, o texto "EPIC FAIL" será enviado para awk .

Este é apenas um exemplo. Substitua o eco por algo apropriado que você possa manipular dentro de awk ou com if read .

    
por 03.07.2012 / 01:32