¿Cómo hacer eco de comandos en un script de shell bash, pero no ejecutarlos?

11

¿Hay alguna manera de ejecutar un script de shell haciendo eco de los comandos pero sin ejecutarlos realmente?

Digamos que tengo un script que elimina un archivo cuyo nombre se almacena en una variable:

#!/bin/bash
set -v
FN="filename"
rm -f ${FN}

Agregar set -vhará eco de los siguientes comandos antes de la ejecución:

$ ./scr.sh
FN="filename"
rm -f ${FN}

Ahora, quiero ver el flujo de este script sin eliminar realmente el archivo. IOW, quiero evitar cualquier efecto en el entorno externo y el sistema de archivos. es posible?

Podría envolver todos los comandos con un echocomando, pero es agotador para un script largo.

ysap
fuente
No soy un experto en scripts de bash, pero si escribiera esto con otro lenguaje de programación, almacenaría todos los comandos en una matriz. De esta manera, debería ser fácil recorrer y ejecutar los comandos o imprimirlos.
hugo der hungrige

Respuestas:

8

No hay forma de recorrer un script para ver cómo se ejecutaría sin hacerlo realmente. En su ejemplo, no hay ifdeclaraciones ni bucles. Pero en los guiones reales, a menudo hay muchas declaraciones condicionales. La rama que se tomará a menudo dependerá de lo que sucedió cuando el shell ejecutó el comando anterior. Si no ejecuta el comando, no hay forma de que el shell sepa qué salida habría generado o cuál habría sido el código de retorno, de lo que podría depender la próxima rama condicional o declaración de asignación.

Si el punto es que desea examinar un script y saber qué hace antes de ejecutarlo, no es una mala idea. Pero, siendo realistas, sobre la mejor manera de hacerlo es simplemente navegar el archivo con lesso vio algo similar.

Adicional

Si está desarrollando una secuencia de comandos y quiere pasar por ella la primera vez, probando la lógica pero sin hacer ningún daño si tiene un error, la solución que a menudo puedo usar es modificar solo las declaraciones que podrían causar algún daño pegando una echoen el frente.

Esto a menudo funciona en scripts típicos de la vida real porque (a) generar las listas de elementos sobre los que iterará o el valor al que establecerá una variable a menudo se puede generar sin cambiar el sistema de archivos y (b) generalmente es suficiente para suponga que si ejecutó el comando, tendría éxito.

Nicole Hamilton
fuente
Gracias. Me sorprendería si hay una manera, exactamente por la razón que mencionaste, pero decidí intentarlo de todos modos. La razón de esto es que estoy desarrollando un script que mueve archivos a través de muchas máquinas en una red y realiza algunas acciones en los hosts remotos. Me gustaría ver un flujo del script antes de confirmar los cambios en los sistemas de archivos. Sería igual de tedioso revisar todas las máquinas remotas para corregir errores de archivo ...
ysap
1
Una adición: para recorrer el guión, puede comenzar el guión con el trap read debugque hará una lectura después de cada línea ejecutada, y luego puede recorrerlo lentamente con enter (esto es útil si tiene un guión realmente largo y desea probar por primera vez)
netigger
8

Creo que va a tener que hacer eco; sin embargo, si coloca el comando en una variable, puede activarlo y desactivarlo. Además, el comando puede ser una función y tan sofisticado como desee.

#!/bin/bash
echo 'Debug'
  D=echo
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy
echo $'\n\nLive'
  D=
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy

!$ > ./conditional.sh
Debug
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy
rm -f testy
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy

Live -rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy ls: cannot access testy: No such file or directory

usuario191016
fuente
Solicité comillas en los comandos echo / don't-echo, y luego tuve que usarlo D=evalen la parte en vivo. Pero buena respuesta!
Jasper
5

Puede rastrear el flujo utilizando la opción -x para bash, pero esto aún ejecutaría los comandos.

Lo mejor que puede hacer es poner eco o comentar los comandos que tienen efectos externos como el rm. Al menos no tendrías que cambiar cada línea.

parkydr
fuente
Gracias. Mencioné esa opción en mi pregunta, pero en realidad no resuelve mi problema.
ysap
Lo siento, pensé que te referías a repetir cada línea, -x reduciría esto a unos pocos comandos y te mostraría las evaluaciones.
parkydr
4

No conozco ningún método en particular para la ejecución en seco, pero podemos tomar algunas precauciones para comprender un script desconocido.

  1. Use un entorno chroot para ejecutar su script de depuración. Actuará como una caja de arena y proporcionará protección contra la eliminación / modificación de los archivos principales del sistema.
  2. Úselo bash -n <script>para la verificación de sintaxis . Del manual de gnu bash https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html

-n Read commands but do not execute them; this may be used to check a script for syntax errors. This option is ignored by interactive shells.

  1. Lee el guión al menos una vez. Puede usar la declaración echo para la depuración como se menciona en la respuesta del usuario191016 .
    • Esté atento a los códigos sospechosos o propensos al peligro.
    • por ejemplo, relacionado con rm, comandos que afectan a / dev / sda, etc.
  2. Siempre intente ejecutar scripts como usuario normal, evite ejecutar scripts desconocidos como root.
  3. Puede definir un alias para hacer un comando que pueda modificar archivos como interactivos al comienzo de su script Ejemplo alias rm="rm -i" alias cp="cp -i" alias mv="mv -i" Para editores de flujo como sed sed -i(-i modifica el archivo en su lugar sin -i hace una ejecución en seco para sed, consulte la página de manual para obtener más detalles) puede copiar el comando completo y ejecutarlo. Justo antes del comando real, mostrará el resultado en la pantalla y luego use el comando para esperar a que continúe la entrada del usuario. Ejemplo sed -i <pattern> Reemplácelo con sed <pattern> read -p "Press any key..." sed -i <pattern>

También siempre se sugiere mantener puntos de respaldo en su sistema para que en caso de emergencia se puedan restaurar los datos.

Rohit Raj Mishra
fuente
3

Haz set –no agrega na tu set –vcomando existente . Pero dado que esto hace que el shell no evalúe ningún comando, ni siquiera ifo while, es posible que no obtenga una vista demasiado buena del flujo operativo.

Scott
fuente
Gracias. Este es un buen comienzo, pero como mencionó, no me deja ver el flujo real, donde en mi script real tengo un bucle sobre una lista de hosts remotos.
ysap
3

Aquí hay una pequeña construcción que me gusta usar:

#!/bin/sh

verbose=false
really=true

# parse arguments
while [ $# -ge 1 ]
do
  case "$1" in
    -v) verbose=true ;;
    -n) really=false; verbose=true ;;
  esac
  shift
done

doCmd() {
  if $verbose; then echo "$@"; fi
  if $really; then "$@"; fi
}

doCmd make foo
doCmd rm -rf /tmp/installdir
doCmd mkdir /tmp/installdir
doCmd whatever
doCmd blah blah
Edward Falk
fuente
2

Si desea evitar que ocurran modificaciones, puede ejecutar el script como usuario sin privilegios:

sudo -u nobody ./scr.sh

En su ejemplo, esto se ejecutará FN="filename"como de costumbre. Si bien técnicamente también ejecuta el comando rm -f ${FN}, no pasará nada si nadie no tiene los permisos necesarios para eliminar el archivo.

Por supuesto, esto causará problemas con el flujo de trabajo condicional. El hecho de que el rmcomando haya fallado podría afectar a otras partes del script.

Dennis
fuente
Cosas que nunca entenderemos: ¿Por qué se requiere root para ejecutarse como nadie ...
Mjohnsonengr
El usuario "nobody" no es especial para el sistema operativo, pero puede agregar una entrada en el archivo sudoers que permita sudo sin contraseña para "nobody".
Dennis
No se. Parece que la secuencia de comandos finalizará debido a algún error u otro antes de que finalice.
Edward Falk