¿Ocultar el resultado de un comando de shell solo en caso de éxito?

12

Ocultar el resultado de un comando de shell generalmente implica redirigir stderr y stdout. ¿Hay alguna instalación o comando incorporado que, de forma predeterminada, oculta la salida pero, por error, voltea toda la salida acumulada? Me gustaría ejecutar esto como un contenedor para sshcomandos remotos . Ahora los tengo usando la redirección, pero no tengo ni idea de qué los hizo fallar, y son demasiado detallados.

EDITAR: Al final, creé la siguiente plantilla basada en la respuesta de @Belmin que modifiqué un poco para acumular todos los comandos anteriores del script, usar el identificador de proceso actual, eliminar automáticamente el registro y agregar un error rojo de falla mensaje cuando algo sale mal. En esta plantilla, los silentcontenedores iniciales tendrán éxito, luego fallarán el tercer comando porque el directorio ya 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
fuente
2
Si redirige solo stdout, stderr seguirá apareciendo; ¿es esto suficiente para ti o quieres ver también stdout si hay un error?
Kromey
Quiero ver ambos, pero solo si algo sale mal, de lo contrario no quiero ver nada.
Grzegorz Adam Hankiewicz
2
Lo que hago es imprimir stdout & stderr en un archivo de registro para que no abarrote la pantalla. También puedo imprimir stderr a la pantalla, así que si hay es y error Puedo verlo. Si necesito detalles, puedo verificar el archivo de registro, que contiene la salida del programa y los errores del programa en contexto. De esta manera, puedo "ver ambos pero solo si algo sale mal". ¿Esto ayuda? Ver stackoverflow.com/questions/2871233/…
Stefan Lasiewski
1
¿Es seguro redirigir stderr y stdout al mismo archivo con dos redireccionamientos separados? Pensé que siempre deberíamos usar 2>&1algo como:$* >>"${SILENT_LOG}" 2>&1" || report_and_exit
psmith

Respuestas:

3

Configuraría una función 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; }

Entonces, puedes ejecutar el comando:

suppress foo -a bar
Belmin Fernandez
fuente
Un atacante que tenga acceso no root en su sistema, podría intentar hacer un enlace simbólico entre rm y call de comando, que apunte a /etc/passswdalgún otro archivo crítico y sobrescriba el contenido.
Mitar
1
Por cierto, el orden de los redireccionamientos anteriores debe ser: $* > /tmp/surpress.out 2>&1Esto realmente captura el stderr.
Mitar
2
$*No es lo mejor manejar entradas arbitrarias. Especialmente cuando contiene espacios o banderas. La mayoría de los portátiles son de ${1+"$@"}acuerdo con stackoverflow.com/questions/743454/…
balrok
Modificado por ambos comentarios. Gracias --- buena información.
Belmin Fernández
7

Debería ser bastante fácil escribir un script para este propósito.

Algo así como este script completamente no probado.

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

Por otro lado, para los comandos que ejecuto como parte de un script, generalmente quiero algo mejor que simplemente imprimir toda la salida. A menudo limito lo que veo a lo desconocido. Aquí hay un guión que adapté de algo que leí hace más de una década.

#!/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
fuente
5

No creo que haya una manera limpia de hacer esto, lo único que puedo pensar es

  • Capture la salida del comando.
  • Verifique el valor de retorno del comando y si falla
    • Muestra la salida capturada.

Sin embargo, implementar esto podría ser un proyecto interesante pero quizás más allá de las preguntas y respuestas.

usuario9517
fuente
Debería ser factible con una función. Hm, déjame intentarlo.
Belmin Fernández
1

quedando corto con algo como tehcommand &>/tmp/$$ || cat /tmp/$$

depende de la facilidad de uso / escritura que desee / necesite. (por ejemplo, usarlo como una tubería o pasar el comando por argumento)

El script corto @zoredache es básicamente un prototipo para esto, lo que daría más solidez, manejaría la concurrencia, etc.

rogerovo
fuente
1

Inténtalo así:

out=`command args...` || echo $out
usuario2743554
fuente
2
Lo escribiría comoout="$(command args...)" || echo "$out"
kasperd