Como faço para capturar o status de retorno e usar tee ao mesmo tempo no shell korn? [duplicado]

7

Considere o código fonte:

1. Parent.sh

#!/usr/bin/ksh
# No tee
ksh Child.sh;
exit_status=$?;
echo "Exit status: ${exit_status}"
# Using tee
ksh Child.sh | tee -a log.txt;
exit_status=$?;
echo "Exit status: ${exit_status}"

2. Child.sh

#!/usr/bin/ksh
...
exit 1;

Saída:

Exit status: 1
Exit status: 0
  • Variável $exit_status está capturando o status de saída de Child.sh e, portanto, é 1 .
  • No segundo caso, $exit_status está capturando o status de saída do tee, que é 0 .

Então, como faço para capturar o status de saída e também uso tee?

    
por Kent Pawar 17.05.2013 / 10:10

2 respostas

14

Reproduzido (e melhorado) da comp.unix.shell FAQ (desde que eu aconteça ter escrito essa seção do FAQ):

Como obtenho o código de saída de cmd1 em cmd1 | cmd2

Primeiro, observe que o código de saída cmd1 pode ser diferente de zero e ainda não significa um erro. Isso acontece, por exemplo, em

cmd | head -n 1

você pode observar um status de saída 141 (ou 269 com ksh93 ou 397 com yash) de cmd , mas é porque cmd foi interrompido por um sinal SIGPIPE quando head -n 1 terminou depois de ler uma linha.

Para saber o status de saída dos elementos de um pipeline

cmd1 | cmd2 | cmd3

com zsh:

Os códigos de saída são fornecidos na matriz pipestatus special. O código de saída cmd1 está em $pipestatus[1] , cmd3 código de saída em $pipestatus[3] , de modo que $? seja sempre o mesmo que $pipestatus[-1] .

com bash:

Os códigos de saída são fornecidos na matriz PIPESTATUS special. O código de saída cmd1 está em ${PIPESTATUS[0]} , cmd3 código de saída em ${PIPESTATUS[2]} , de modo que $? seja sempre o mesmo que ${PIPESTATUS[-1]} (ou ${PIPESTATUS[@]: -1} para versões anteriores a 4.2).

com qualquer outro Bourne como shells

Você precisa usar um truque para passar os códigos de saída para o shell principal. Você pode fazer usando um tubo (2). Em vez de executar cmd1 , você executa cmd1; echo "$?" e faz certeza $? faz o seu caminho para o shell.

exec 3>&1
code='
  # now, inside the backticks, fd4 goes to the pipe
  # whose other end is read and stored in $code  for
  # later evaluation; fd1 is the normal standard output
  # preserved the line before with exec 3>&1

  exec 4>&1 >&3 3>&- 
  {
    cmd1 4>&-; echo "ec1=$?;" >&4
  } | {
    cmd2 4>&-; echo "ec2=$?;" >&4
  } | cmd3 4>&-
  echo "ec3=$?;" >&4
'
exec 3>&-
eval "$code"

Saia dos códigos em $ec1 , $ec2 , $ec3 .

com um shell POSIX

Você pode usar essa função para facilitar:

run() {
  j=1
  while eval "\${pipestatus_$j+:} false"; do
    unset "pipestatus_$j"
    j=$(($j+1))
  done
  j=1 com= k=1 l=
  for arg do
    case $arg in
      ('|')
        com="$com {
               $l "'3>&-
               echo "pipestatus_'$j'=$?" >&3
             } 4>&- |'
        j=$(($j+1)) l=;;
      (*)
        l="$l \"\${$k}\""
    esac
    k=$(($k+1))
  done
  com="$com $l"' 3>&- >&4 4>&-
       echo "pipestatus_'$j'=$?"'

  { eval "$(exec 3>&1; eval "$com")"; } 4>&1
  j=1
  ret=0
  while eval "\${pipestatus_$j+:} false"; do
    eval '[ "$pipestatus_'"$j"'" -eq 0 ] || ret=$pipestatus_'"$j"
    j=$(($j+1))
  done
  return "$ret"
}

Use como:

run cmd1 \| cmd2 \| cmd3

códigos de saída estão em $pipestatus_1 , $pipestatus_2 , $pipestatus_3 e $? é o status de saída diferente de zero (como na opção pipefail de alguns shells).

    
por 17.05.2013 / 11:51
-1

Você pode usar & & e || para fazer isso no bash - eu assumo que algo similar funcionará no ksh.

ksh Child.sh && exit_status="$?" || exit_status="$?" | tee -a log.txt;

EDITAR:

Como @Stephane aponta, A && B | C irá gerar A para stdout, e somente o pipe B para C. Ele precisa agrupar as saídas, para juntá-las. Você não pode passar uma variável de um subshell para o chamador, mas você pode fazer isso:

x=$(tempfile) && exit_status=$(ksh Child.sh > $x; echo $?) && (cat $x; rm $x) | tee -a log.txt
    
por 17.05.2013 / 11:18