Ocultar a saída de um comando shell apenas com sucesso?

12

Ocultar a saída de um comando shell geralmente envolve redirecionar stderr e stdout. Existe algum recurso ou comando interno que, por padrão, oculta a saída, mas que, por erro, despeja toda a saída acumulada? Gostaria de executar isso como um invólucro para sshcomandos remotos . Agora eu os tenho usando o redirecionamento, mas não tenho idéia do que os fez falhar, e eles são muito detalhados.

EDIT: No final, criei o seguinte modelo com base na resposta de @Belmin, que aprimorei um pouco para acumular todos os comandos anteriores do script, use o identificador de processo atual, remova automaticamente o log e adicione um erro vermelho de falha mensagem quando algo der errado. Neste modelo, os silentwrappers iniciais serão bem-sucedidos e falharão no terceiro comando porque o diretório já existe:

#!/bin/sh

set -e

SILENT_LOG=/tmp/silent_log_$$.txt
trap "/bin/rm -f $SILENT_LOG" EXIT

function report_and_exit {
    cat "${SILENT_LOG}";
    echo "\033[91mError running command.\033[39m"
    exit 1;
}

function silent {
    $* 2>>"${SILENT_LOG}" >> "${SILENT_LOG}" || report_and_exit;
}

silent mkdir -v pepe
silent mkdir -v pepe2
silent mkdir -v pepe
silent mkdir -v pepe2
Grzegorz Adam Hankiewicz
fonte
2
Se você redirecionar apenas o stdout, o stderr ainda aparecerá; isso é suficiente para você ou você deseja ver o stdout também se houver um erro?
Kromey
Quero ver os dois, mas apenas se algo der errado, caso contrário, não quero ver nada.
Grzegorz Adam Hankiewicz
2
O que faço é imprimir stdout e stderr em um arquivo de log para que não atrapalhe a tela. Também imprimo stderr na tela, portanto, se houver um erro, posso vê-lo. Se precisar de detalhes, posso verificar o arquivo de log, que contém a saída do programa e os erros do programa no contexto. Dessa forma, eu posso 'ver os dois, mas apenas se algo der errado'. Isso ajuda? Veja stackoverflow.com/questions/2871233/…
Stefan Lasiewski
1
É seguro redirecionar stderr e stdout para o mesmo arquivo com dois redirecionamentos separados? Eu pensei que nós sempre deve usar 2>&1, algo como:$* >>"${SILENT_LOG}" 2>&1" || report_and_exit
psmith

Respostas:

3

Eu configuraria uma função bash como esta:

function suppress { /bin/rm --force /tmp/suppress.out 2> /dev/null; ${1+"$@"} > /tmp/suppress.out 2>&1 || cat /tmp/suppress.out; /bin/rm /tmp/suppress.out; }

Então, você pode simplesmente executar o comando:

suppress foo -a bar
Belmin Fernandez
fonte
Um invasor que tenha acesso não raiz ao seu sistema pode tentar fazer um link simbólico entre rm e chamada de comando, o que apontaria para /etc/passswdalgum outro arquivo crítico e substituiria o conteúdo.
precisa
1
BTW, a ordem dos redirecionamentos acima deve ser: $* > /tmp/surpress.out 2>&1Isso realmente captura o stderr.
Mitar
2
$*não é o melhor para lidar com entradas arbitrárias. Especialmente quando contém espaços ou sinalizadores. A maioria dos portáteis está de ${1+"$@"}acordo com stackoverflow.com/questions/743454/…
balrok 17/17
Modificado por ambos os comentários. Obrigado --- boa informação.
Belmin Fernandez 18/10/19
7

Deve ser fácil escrever um script para esse fim.

Algo como esse script completamente não testado.

OUTPUT=`tempfile`
program_we_want_to_capture &2>1 > $OUTPUT
[ $? -ne 0 ]; then
    cat $OUTPUT
    exit 1
fi
rm $OUTPUT

Por outro lado, para os comandos executados como parte de um script, geralmente quero algo melhor do que simplesmente imprimir toda a saída. Costumo limitar o que vejo ao desconhecido. Aqui está um roteiro que eu adaptei de algo que li mais de uma década atrás.

#!/bin/bash

the_command 2>&1 | awk '
BEGIN \
{
  # Initialize our error-detection flag.
  ErrorDetected = 0
}
# Following are regex that will simply skip all lines
# which are good and we never want to see
/ Added UserList source/ || \
/ Added User/ || \
/ init domainlist / || \
/ init iplist / || \
/ init urllist / || \
/ loading dbfile / || \
/^$/ {next} # Uninteresting message.  Skip it.

# Following are lines that we good and we always want to see
/ INFO: ready for requests / \
{
  print "  " $0 # Expected message we want to see.
  next
}

# any remaining lines are unexpected, and probably error messages.  These will be printed out and highlighted.
{
  print "->" $0 # Unexpected message.  Print it
  ErrorDetected=1
}

END \
{
  if (ErrorDetected == 1) {
    print "Unexpected messages (\"->\") detected in execution."
    exit 2
  }
}
'
exit $?
Zoredache
fonte
5

Eu não acho que exista uma maneira limpa de fazer isso, a única coisa que consigo pensar é

  • Capture a saída do comando.
  • Verifique o valor de retorno do comando e se ele falhou
    • exibir a saída capturada.

Implementar isso pode ser um projeto interessante, mas talvez além das perguntas e respostas.

user9517
fonte
Deve ser factível com uma função. Hum, deixe-me tentar.
Belmin Fernandez
1

indo curto com algo como tehcommand &>/tmp/$$ || cat /tmp/$$

depende de quanto usabilidade / digitação você deseja / precisa. (por exemplo, usando-o como um canal ou passando o comando por argumento)

O script curto do @zoredache é basicamente um proto-wrapper para isso, o que daria mais robustez, lida com a simultaneidade, etc.

Rogerovo
fonte
1

Tente assim:

out=`command args...` || echo $out
user2743554
fonte
2
Eu escreveria comoout="$(command args...)" || echo "$out"
kasperd 26/06