¿Cómo puedo suprimir la salida solo si el comando tiene éxito?

22

Me gustaría simplificar la salida de un script suprimiendo la salida de comandos secundarios que generalmente son exitosos.

Sin embargo, -qusarlos oculta la salida cuando ocasionalmente fallan, por lo que no tengo forma de entender el error. Además, estos comandos registran su salida stderr.

¿Hay alguna forma de suprimir la salida de un comando solo si tiene éxito ?

Por ejemplo (pero no limitado a) algo como esto:

mycommand | fingerscrossed

Si todo va bien, fingerscrossedcaptura la salida y la descarta. De lo contrario, se hace eco a la salida estándar o de error (lo que sea).

Matthieu Napoli
fuente

Respuestas:

36

moreutils' chroniccomando hace exactamente eso:

chronic mycommand

tragará mycommandla salida, a menos que falle, en cuyo caso se mostrará la salida.

Stephen Kitt
fuente
1
Gracias. ¿Supongo que no está instalado por defecto en la mayoría de los sistemas operativos Unix?
Matthieu Napoli
1
Probablemente no, aunque está muy empaquetado, por lo que debería ser fácil de instalar.
Stephen Kitt el
1
Debian lo tiene en el paquete moreutils. Agradable para mí, de todos modos :)
Tom Zych
66
Tenga en cuenta que almacena toda la salida en la memoria.
Stéphane Chazelas
1
@ StéphaneChazelas es probablemente la única forma de implementar algo como esto, la salida debe almacenarse mientras se ejecuta el comando en caso de que sea necesario.
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))"

Eso probablemente debería hacer el truco. Almacenará en búfer la salida de cada uno commanden un archivo temporal eliminado, y luego desviará su salida en cualquiera /dev/nullo stderr dependiendo de si su estado de retorno no fue cero. Debido a que el archivo temporal se elimina con anticipación, no puede ser leído por ningún proceso que no sea el shell actual y sus elementos secundarios en su descriptor de archivo (salvo los /proc/$pid/fdsnoops furtivos con los permisos apropiados) , y no requiere limpieza cuando finalice.

Quizás una solución más conveniente en sistemas Linux:

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

... que, en la mayoría de conchas, funciona de manera similar al otro, a que se le puede llamar como: divert some simple-command with args. Tenga cuidado con los comandos de alto rendimiento en "$@", sin embargo, para dash, yashu otros shells que hacen documentos aquí con tuberías: creo que es posible que esos shells llenen el búfer de tuberías (por defecto, alrededor de 128 kb en linuxes) y por lo tanto un punto muerto . Eso no debería ser una preocupación para ksh, mksh,bash , zsh, o el shell Bourne, aunque - todos los hacen básicamente lo mismo que hice explícitamente más arriba con exec.

mikeserv
fuente
9

Por lo general, en caso de error, el comando emite mensajes, por stderrlo que para su tarea puede simplemente suprimirstdout

mycommand > /dev/null
Costas
fuente
99
Tenga cuidado
PyRulez
Gracias, pero como dije en la pregunta, mis comandos registran toda la salida stderr(por lo que no tiene ningún efecto).
Matthieu Napoli
4

Para hacer tu propia 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
fuente
3

Hago algo como esto en mis 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 eso a su situación, podría hacer algo como esto:

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

Entonces, "if" ejecuta el comando y canaliza la salida a mycommand.log. Si necesita atrapar stdout vs stdout vs lo que sea, es posible que deba cambiar el comando de tubería '&>' a '>'. Si el comando falla, capture el código de error, imprima el contenido de mycommand.log, elimine mycommand.log y finalmente regrese con el código de error original.

Sin la (salida $ c), regresaría con el código de salida que coincide con lo que devolvió el comando 'rm'.

Finalmente, si quieres un trazador de líneas, algo como esto funcionaría.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log
Jordán
fuente
2
¿Realmente envuelve sus comandos / etc. en (...)asi? Porque no hace nada útil para ti, sino que genera subcapas adicionales.
Etan Reisner
@EtanReisner (exit $c)está configurando $?, que es algo que no puede hacer de otra manera. if ! (mycommand) &>xes significativo con la redirección si el comando usa, por ejemplo, timeo daría un error de shell.
Michael Homer
@MichaelHomer: para esas cosas están { ; }los curlies ... aunque exites un poco complicado allí, lo admito.
mikeserv
En un fragmento de archivo MAKE, si está intentando salir con el guardado anteriormente $?, puede usarlo, exit $cpero sí, en otros casos (exit $?)tiene valor (aunque una función de shell rret() { return $1; }sería mejor en general, diría). La subshell para comandos todavía no funciona realmente como lo indicó mikeserv.
Etan Reisner
3

Acabo de encontrar esta respuesta mucho más simple en esta otra pregunta :

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

¡Funciona de maravilla!

Matthieu Napoli
fuente
Nota: para mi caso de uso, esta fue una solución mucho más simple (evite instalar cosas adicionales en todos los servidores CI), así que decidí marcar esta como aceptada. YMMV.
Matthieu Napoli
Tenga en cuenta que si utiliza set -o xtrace en su script de shell, toda la salida estará allí nuevamente como parte del registro de los detalles de la salida de asignación = ... :-). En ese caso, probablemente sea mejor usar crónica.
Jan-Philip Gehrcke