bash – ¿Cuál es una forma conveniente de ejecutar una larga lista de comandos y mostrar un mensaje si algo sale mal?

Pregunta:

La mayoría de las guías de Linux constan de páginas como "necesitas ejecutar command_1 , luego command_2 , luego command_3 ", etc. Como no quiero perder el tiempo ejecutándolos todos manualmente, prefiero crear un script

command_1
command_2
command_3

y ejecútelo una vez. Pero, la mayoría de las veces, algunos comandos fallarán y no tendré idea de qué comandos fallaron. Además, por lo general, todos los demás comandos no tienen sentido si algo falló antes. Entonces, un mejor guión sería algo como

   (command_1 && echo OK command_1 || (echo FAILED command_1; false) )
&& (command_2 && echo OK command_2 || (echo FAILED command_2; false) )
&& (command_3 && echo OK command_3 || (echo FAILED command_3; false) )
&& echo DONE 
|| echo FAILED

Pero requiere escribir demasiado código repetitivo, repetir cada comando 3 veces, y hay demasiadas posibilidades de que escriba mal algunas de las llaves. ¿Existe una forma más conveniente de hacer lo que hace el último script? En particular:

  • ejecutar comandos secuencialmente
  • romper si falla algún comando
  • escribir, qué comando ha fallado, si alguno
  • Permite interacciones normales con comandos: imprime toda la salida y permite la entrada desde el teclado, si el comando pregunta algo.

Resumen de respuestas (2 de enero de 2020)

Hay 2 tipos de soluciones:

  • Aquellos que permiten copiar y pegar comandos de la guía sin modificaciones, pero no imprimen el comando fallido al final. Entonces, si el comando fallido produjo una salida muy larga, tendrá que desplazarse muchas líneas hacia arriba para ver qué comando falló. (Todas las respuestas principales)
  • Aquellos que imprimen el comando fallido en la última línea, pero requieren que modifique los comandos después de copiarlos y pegarlos, ya sea agregando comillas (respuesta de John) o agregando declaraciones de try y dividiendo comandos encadenados en comandos separados (respuesta de Jasen ).

Amigos de rock, pero dejaré esta pregunta abierta por un tiempo. Tal vez alguien conozca una solución que satisfaga ambas necesidades (imprimir comando fallido en la última línea y permitir copiar y pegar comandos sin sus modificaciones).

Respuesta:

Una opción sería poner los comandos en un script bash e iniciarlo con set -e .

Esto hará que el script finalice antes de tiempo si algún comando sale con un estado de salida distinto de cero.

Consulte también esta pregunta sobre el desbordamiento de pila: https://stackoverflow.com/q/19622198/828193

Para imprimir el error, puede utilizar

trap 'do_something' ERR

Donde do_something es un comando que crearía para mostrar el error.

A continuación, se muestra un ejemplo de un script para ver cómo funciona:

#!/bin/bash

set -e
trap 'echo "******* FAILED *******" 1>&2' ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to set -e

Y esta es la salida:

$ ./test.sh 
Command that succeeds
ls: cannot access 'non_existent_file': No such file or directory
******* FAILED *******

Además, como lo menciona @jick , tenga en cuenta que el estado de salida de una canalización es por defecto el estado de salida del comando final en ella. Esto significa que si un comando no final en la canalización falla, set -e . Para solucionar este problema si le preocupa, puede usar set -o pipefail


Como sugirió mi @glenn jackman y @Monty Harder , usar una función como controlador puede hacer que el script sea más legible, ya que evita las citas anidadas. Como estamos usando una función de todos modos ahora, eliminé el set -e completo y usé la exit 1 en el controlador, lo que también podría hacerlo más legible para algunos:

#!/bin/bash

error_handler() {
  echo "******* FAILED *******" 1>&2
  exit 1
}

trap error_handler ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to the exit in the handler

La salida es idéntica a la anterior, aunque el estado de salida del script es diferente.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top

web tasarım