Como posso suprimir a saída apenas se o comando for bem-sucedido?


22

Gostaria de simplificar a saída de um script suprimindo a saída de comandos secundários que geralmente são bem-sucedidos.

No entanto, usá -q-los oculta a saída quando eles ocasionalmente falham, então não tenho como entender o erro. Além disso, esses comandos registram sua saída stderr.

Existe uma maneira de suprimir a saída de um comando apenas se for bem-sucedida ?

Por exemplo (mas não limitado a) algo como isto:

mycommand | fingerscrossed

Se tudo correr bem, fingerscrossedpega a saída e a descarta. Caso contrário, ele ecoará na saída padrão ou de erro (qualquer que seja).

Respostas:


36

moreutilsO chroniccomando ' faz exatamente isso:

chronic mycommand

engolirá mycommanda saída, a menos que falhe; nesse caso, a saída será exibida.


1
Obrigado. Presumo que ele não esteja instalado por padrão na maioria dos sistemas operacionais Unix?
— Matthieu Napoli

1
Provavelmente não, embora seja amplamente empacotado, por isso deve ser fácil de instalar.
— Stephen Kitt

1
O Debian tem no pacote moreutils. Bom para mim, de qualquer maneira :)
— Tom Zych

6
Observe que ele armazena toda a saída na memória.
— Stéphane Chazelas

1
@ StéphaneChazelas, que provavelmente é a única maneira de implementar algo assim, a saída precisa ser armazenada enquanto o comando estiver em execução, caso seja necessário.
— Centimane

11
### do this bit once at the top of your script
divert=
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

Isso provavelmente deve funcionar. Ele armazenará em buffer a saída de cada commandum em um arquivo temporário excluído e, posteriormente, desviará a saída para /dev/nullstderr, dependendo de seu status de retorno não ser zero. Como o arquivo temporário é excluído antecipadamente, ele não pode ser lido por nenhum processo, exceto pelo shell atual e seus filhos em seu descritor de arquivo (barrando /proc/$pid/fdbisbilhoteiros sorrateiros com permissões apropriadas) , e não requer limpeza durante o processo.

Talvez uma solução mais conveniente em sistemas Linux:

divert(){
    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

... que, na maioria das conchas, funciona muito como o outro, exceto que você pode chamá-lo assim: divert some simple-command with args. Cuidado com os comandos de alta saída "$@", porém, para dash, yashou alguns outros shells que aqui documentam com pipes - acho que nesses shells pode ser possível preencher o buffer do pipe (com um padrão de cerca de 128 kb em linuxes) e, portanto, um impasse . Isso não deve ser uma preocupação para ksh, mksh, bash, zsh, ou o shell Bourne, embora - todos aqueles fazem basicamente a mesma coisa que eu fiz explicitamente acima com exec.


9

Normalmente, em caso de erro, o comando envia mensagens para stderrque, para sua tarefa, você possa simplesmente suprimirstdout

mycommand > /dev/null


Obrigado, mas como eu disse na pergunta, meus comandos registram toda a saída stderr(portanto, não tem efeito).
— Matthieu Napoli 19/01

4

Para fazer sua própria crônica

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  ret=$?
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command
}

3

Eu faço algo assim nos meus makefiles:

if (mycommand) &> mycommand.log; then 
  echo success 
else 
  c=$?; 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  command_to_run_on_fail
  (exit $c)
fi

Adaptando isso à sua situação, você pode fazer algo assim:

if ! (mycommand) &> mycommand.log; then 
  c=$?; 
  cat mycommand.log
  rm mycommand.log
  (exit $c)
fi

Portanto, "if" executa o comando e canaliza a saída para mycommand.log. Se você precisar capturar stdout vs stdout vs o que for, pode ser necessário alterar o comando de pipe '&>' para '>'. Se o comando falhar, capture o código de erro, imprima o conteúdo de mycommand.log, remova mycommand.log e, finalmente, retorne com o código de erro original.

Sem o (exit $ c) você retornaria com o código de saída que corresponde ao que o comando 'rm' retornou.

Finalmente, se você quiser um liner, algo assim funcionaria.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log

2
Você realmente quebra seus comandos / etc. em (...)assim? Porque ele não faz nada útil para você, mas gera subcascas extras.
— Etan Reisner

O @EtanReisner (exit $c)está definido $?, o que é algo que você não pode fazer de outra maneira. if ! (mycommand) &>xé significativo com o redirecionamento se o comando usar, por exemplo, timeou der um erro de shell.
— Michael Homer

{ ; }@ MichaelHomer - para essas coisas existem os curlies ... embora exitseja um pouco complicado lá, é certo.
— precisa saber é

Em um trecho de makefile, se você estiver tentando sair com o salvo anteriormente $?, poderá usar, exit $cmas sim, em outros casos (exit $?)tem valor (embora uma função shell rret() { return $1; }seja melhor em geral, eu diria). O subshell para comandos ainda não parece realmente como mikeserv indicou.
— Etan Reisner 20/01

3

Acabei de encontrar esta resposta muito mais simples nesta outra questão :

output=`mycommand 2>&1` || echo $output

Funciona como um encanto!


Nota: para o meu caso de uso, essa era uma solução muito mais simples (evite instalar coisas extras em todos os servidores de IC), por isso escolhi marcar essa como aceita. YMMV.
— Matthieu Napoli 22/01

Note que se você usar set -o xtrace no seu script shell, toda a saída estará lá novamente como parte do registro dos detalhes da saída da atribuição = ... :-). Nesse caso, provavelmente é melhor usar crônico.
— Jan-Philip Gehrcke
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.