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).

Matthieu Napoli
fonte

Respostas:

36

moreutilsO chroniccomando ' faz exatamente isso:

chronic mycommand

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

Stephen Kitt
fonte
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.

mikeserv
fonte
9

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

mycommand > /dev/null
Costas
fonte
9
Tome cuidado
PyRulez
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
}
Jacob Minshall
fonte
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
Jordânia
fonte
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!

Matthieu Napoli
fonte
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