Cómo usar eval en Linux Bash Scripts

Publicado: 2022-08-22
Computadora portátil Linux que muestra un indicador de bash
fatmawati achmad zaenuri/Shutterstock.com

De todos los comandos de Bash, el pobre viejo eval probablemente tenga la peor reputación. ¿Justificado o simplemente mala prensa? Discutimos el uso y los peligros de este menos querido de los comandos de Linux.

Necesitamos hablar sobre la evaluación

Usado sin cuidado, eval puede conducir a un comportamiento impredecible e incluso a inseguridades en el sistema. Por lo que parece, probablemente no deberíamos usarlo, ¿verdad? Bueno, no del todo.

Se podría decir algo similar sobre los automóviles. En las manos equivocadas, son un arma mortal. La gente los usa en ataques de ariete y como vehículos de huida. ¿Deberíamos todos dejar de usar automóviles? No claro que no. Pero tienen que ser utilizados correctamente y por personas que sepan conducirlos.

Cómo trabajar con variables en Bash
RELACIONADO Cómo trabajar con variables en Bash

El adjetivo habitual que se aplica a eval es “mal”. Pero todo se reduce a cómo se usa. El comando eval recopila los valores de una o más variables. Crea una cadena de comandos. Luego ejecuta ese comando. Esto lo hace útil cuando necesita hacer frente a situaciones en las que el contenido de un comando se deriva dinámicamente durante la ejecución de su secuencia de comandos.

Los problemas surgen cuando se escribe una secuencia de comandos para usar eval en una cadena que se ha recibido desde algún lugar fuera de la secuencia de comandos. Puede ser ingresado por un usuario, enviado a través de una API, etiquetado en una solicitud HTTPS o en cualquier otro lugar externo al script.

Si la cadena en la que va a funcionar eval no se derivó localmente y mediante programación, existe el riesgo de que la cadena contenga instrucciones maliciosas incrustadas u otra entrada mal formada. Obviamente, no desea que eval ejecute comandos maliciosos. Entonces, para estar seguro, no use eval con cadenas generadas externamente o entrada del usuario.

Primeros pasos con eval

El comando eval es un comando de shell Bash integrado. Si Bash está presente, eval estará presente.

eval concatena sus parámetros en una sola cadena. Utilizará un solo espacio para separar elementos concatenados. Evalúa los argumentos y luego pasa la cadena completa al shell para ejecutar.

Vamos a crear una variable llamada wordcount .

 wordcount="wc -w raw-notes.md"

La variable de cadena contiene un comando para contar las palabras en un archivo llamado "raw-notes.md".

Podemos usar eval para ejecutar ese comando pasándole el valor de la variable.

 eval " $wordcount "

Usando eval con una variable de cadena para contar las palabras en un archivo

El comando se ejecuta en el shell actual, no en un subshell. Podemos mostrar esto fácilmente. Tenemos un archivo de texto corto llamado "variables.txt". Contiene estas dos líneas.

 primero = instrucciones
segundo = friki

Usaremos cat para enviar estas líneas a la ventana de terminal. Luego usaremos eval para evaluar un comando cat para que se sigan las instrucciones dentro del archivo de texto. Esto establecerá las variables para nosotros.

 gato variables.txt
eval "$(cat variables.txt)"
echo $primero $segundo 

Acceso a variables establecidas por eval en el shell actual

Al usar echo para imprimir los valores de las variables, podemos ver que el comando eval se ejecuta en el shell actual, no en un subshell.

Un proceso en una subcapa no puede cambiar el entorno de la capa principal. Debido a que eval se ejecuta en el shell actual, las variables establecidas por eval se pueden usar desde el shell que lanzó el comando eval .

Tenga en cuenta que si usa eval en un script, el shell que sería alterado por eval es el subshell en el que se ejecuta el script, no el shell que lo inició.

RELACIONADO: Cómo usar los comandos cat y tac de Linux

Uso de variables en la cadena de comandos

Podemos incluir otras variables en las cadenas de comandos. Estableceremos dos variables para contener números enteros.

 número1=10 
número2=7

Crearemos una variable para contener un comando expr que devolverá la suma de dos números. Esto significa que necesitamos acceder a los valores de las dos variables enteras en el comando. Tenga en cuenta los acentos graves alrededor de la instrucción expr .

 add="`expr $num1 + $num2`"

Crearemos otro comando para mostrarnos el resultado de la instrucción expr .

 mostrar = "eco"

Tenga en cuenta que no necesitamos incluir un espacio al final de la cadena echo , ni al comienzo de la cadena expr . eval se encarga de eso.

Y para ejecutar todo el comando usamos:

 evaluar $mostrar $añadir 

Uso de variables en la cadena de comandos

Los valores de las variables dentro de la cadena expr se sustituyen en la cadena por eval , antes de pasar al shell para que se ejecute.

RELACIONADO: Cómo trabajar con variables en Bash

Acceso a variables dentro de variables

Puede asignar un valor a una variable y luego asignar el nombre de esa variable a otra variable. Usando eval , puede acceder al valor contenido en la primera variable, desde su nombre, que es el valor almacenado en la segunda variable. Un ejemplo le ayudará a desenredar eso.

Copie este script en un editor y guárdelo como un archivo llamado "assign.sh".

 #!/bin/bash

title="Geek de instrucciones"
página web = título
comando = "eco"
eval $comando \${$página web}

Necesitamos hacerlo ejecutable con el comando chmod .

 chmod +x asignar.sh 

Usando chmod para hacer un script ejecutable

Deberá hacer esto para cualquier secuencia de comandos que copie de este artículo. Simplemente use el nombre de script apropiado en cada caso.

Cuando ejecutamos nuestro script, vemos el texto del title de la variable a pesar de que el comando eval está usando la webpage de la variable.

 ./asignar.sh 

Acceder al valor de una variable a partir de su nombre almacenado en otra variable

El signo de dólar escapado “ $ ” y las llaves “ {} ” hacen que eval mire el valor contenido dentro de la variable cuyo nombre está almacenado en la variable de la webpage .

Uso de variables creadas dinámicamente

Podemos usar eval para crear variables dinámicamente. Este script se llama "loop.sh".

 #!/bin/bash

total=0
label="Bucle completo. Total:"

para n en {1..10}
hacer
  evaluar x$n=$n
  echo "Bucle" $x$n
  ((total+=$x$n))
hecho

eco $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $etiqueta $total

Crea una variable llamada total que contiene la suma de los valores de las variables que creamos. Luego crea una variable de cadena llamada label . Esta es una simple cadena de texto.

Vamos a repetir 10 veces y crear 10 variables llamadas x1 hasta x10 . La instrucción eval en el cuerpo del bucle proporciona la "x" y toma el valor del contador de bucle $n para crear el nombre de la variable. Al mismo tiempo, establece la nueva variable en el valor del contador de bucle $n .

9 ejemplos de bucles for en Linux Bash Scripts
RELACIONADO 9 Ejemplos de bucles for en scripts Bash de Linux

Imprime la nueva variable en la ventana del terminal y luego incrementa la variable total con el valor de la nueva variable.

Fuera del ciclo, las 10 nuevas variables se imprimen una vez más, todas en una línea. Tenga en cuenta que también podemos referirnos a las variables por sus nombres reales, sin usar una versión calculada o derivada de sus nombres.

Finalmente, imprimimos el valor de la variable total .

 ./bucle.sh 

Usando eval para crear variables dinámicamente

RELACIONADO: Cartilla: Bash Loops: for, while y till

Uso de eval con arreglos

Imagine un escenario en el que tiene un script que se ejecuta durante mucho tiempo y realiza algún procesamiento por usted. Escribe en un archivo de registro con un nombre creado a partir de una marca de tiempo. De vez en cuando, se iniciará un nuevo archivo de registro. Cuando finaliza el script, si no ha habido errores, borra los archivos de registro que ha creado.

No desea que simplemente haga rm *.log , solo desea que elimine los archivos de registro que ha creado. Este script simula esa funcionalidad. Esto es "clear-logs.sh".

 #!/bin/bash

declarar -a archivos de registro

recuento de archivos = 0 
rm_string="eco"

función crear_archivo_de_registro() {
  ((++recuento de archivos))
  nombre de archivo=$(fecha +"%Y-%m-%d_%H-%M-%S").log
  archivos de registro[$filecount]=$nombre de archivo
  echo $filecount "Creado" ${logfiles[$filecount]}
}

# cuerpo del guión. Aquí se realiza algún procesamiento que
# genera periódicamente un archivo de registro. Lo simularemos
crear_archivo_de_registro
dormir 3
crear_archivo_de_registro
dormir 3
crear_archivo_de_registro
dormir 3
crear_archivo_de_registro

# ¿Hay algún archivo para eliminar?
para ((archivo=1; archivo<=$cuentaarchivos; archivo++))
hacer
  # eliminar el archivo de registro
  eval $rm_string ${archivos de registro[$archivo]} "eliminado..."
  archivos de registro[$archivo]=""
hecho

El script declara una matriz llamada logfiles . Esto contendrá los nombres de los archivos de registro creados por el script. Declara una variable llamada filecount . Esto contendrá la cantidad de archivos de registro que se han creado.

También declara una cadena llamada rm_string . En un script del mundo real, esto contendría el comando rm , pero estamos usando echo para poder demostrar el principio de una manera no destructiva.

La función create_logfile() es donde se nombra cada archivo de registro y dónde se abriría. Solo estamos creando el nombre del archivo y fingiendo que se ha creado en el sistema de archivos.

La función incrementa la variable filecount . Su valor inicial es cero, por lo que el primer nombre de archivo que creamos se almacena en la posición uno de la matriz. Esto se hace a propósito, como veremos más adelante.

El nombre del archivo se crea usando el comando de date y la extensión ".log". El nombre se almacena en la matriz en la posición indicada por filecount . El nombre se imprime en la ventana del terminal. En un script del mundo real, también crearía el archivo real.

Cómo pausar un script Bash con el comando de suspensión de Linux
RELACIONADO Cómo pausar un script Bash con el comando de suspensión de Linux

El cuerpo del script se simula mediante el comando de sleep . Crea el primer archivo de registro, espera tres segundos y luego crea otro. Crea cuatro archivos de registro, espaciados para que las marcas de tiempo en sus nombres de archivo sean diferentes.

Finalmente, hay un bucle que elimina los archivos de registro. El archivo de contador de bucles se establece en uno. Cuenta hasta e incluyendo el valor de filecount , que contiene la cantidad de archivos que se crearon.

Si el número de filecount aún se establece en cero, porque no se crearon archivos de registro, el cuerpo del bucle nunca se ejecutará porque uno no es menor o igual que cero. Es por eso que la variable filecount se estableció en cero cuando se declaró y por qué se incrementó antes de que se creara el primer archivo.

Dentro del ciclo, usamos eval con nuestro rm_string no destructivo y el nombre del archivo que se recupera de la matriz. Luego establecemos el elemento de la matriz en una cadena vacía.

Esto es lo que vemos cuando ejecutamos el script.

 ./borrar-registros.sh 

Eliminación de archivos cuyos nombres están almacenados en una matriz

No todo es malo

Eval muy eval definitivamente tiene sus usos. Como la mayoría de las herramientas, si se usa de manera imprudente, es peligrosa y en más de un sentido.

Si se asegura de que las cadenas en las que funciona se crean internamente y no se capturan de humanos, API o cosas como solicitudes HTTPS, evitará las principales trampas.

RELACIONADO: Cómo mostrar la fecha y la hora en la terminal de Linux (y usarla en secuencias de comandos Bash)