PRIMEROS SCRIPTS

Ya vimos que con touch y cat podemos crear un archivo con extensión .sh (shell) y escribir código para poder ejecutarlo desde consola. Dado que en Linux las extensiones no importan tanto como el contenido en sí, incluso podríamos dejarlo sin la extensión .sh o hasta podríamos ponerle .jpeg y aún así se podría ejecutar sin problemas. Sin embargo, lo correcto es agregarle siempre la extensión correspondiente.

Para ejecutar un script utilizamos el comando ./arch o bien bash arch, pero dado que por defecto ningún script recién creado tiene permisos de ejecución, antes necesitamos otorgarle permisos de ejecución al archivo con chmod +x o bien con chmod 755 arch (¿recuerdas a qué permisos corresponde?)

Todo código debe comenzar con el encabezado #!/bin/bash. La combinación #! se conoce como shebang y /bin/bash es la ruta que le indica al sistema cuál es el intérprete que debe utilizar (en nuestro caso es Bash, pero puede usarse el shebang para otros intérpretes). Es importante que se escriba siempre en la primera línea y sin espacios de por medio.

El código se escribe una sentencia por línea, tal como se haría en la consola. Los comentarios se indican con el signo numeral (#) y abarcan sólo una línea. Hay que remarcar además la importancia de los espacios y las indentaciones.

#!/bin/bash

# código a ejecutar 
NOTA: Cualquier cosa que se pueda ejecutar desde la terminal se puede incluir en un script y viceversa.

Teniendo en cuenta esto, una manera de probar que el código funciona es ir ejecutándolo parte por parte desde consola, verificando que cada paso es realizado correctamente.

Variables

Comando Descripción
var=valor Se declara una variable y se le asigna un valor. Si la variable ya está declarada se le asigna un valor nuevo.
ATENCIÓN: Recordar que no debe haber espacios que si el valor tiene más de una palabra debe escribirse entre comillas
var= Se declara una variable pero no se le asigna ningún valor
$var Invoca a una variable
var=$(com) Almacena el resultado de un comando en una variable. Por ejemplo, fecha=$(date) guarda la fecha actual
readonly VAR=valor Crea una variable cuyo valor no se podrá modificar (constante) [1]
read var1 var2... Lee los datos introducidos por el usuario y los almacena en variables
read -p (-prompt) Muestra el prompt en la misma línea
read -s (-silent) Oculta los caracteres ingresados (como en una contraseña)
compgen -v (-variables) Muestra un listado de todas las variables de entorno
${var,} (con una sola coma) Muestra la primera letra del valor de la variable en minúscula [2]
${var,,} (con dos comas) Muestra el valor de la variable en minúsculas [2]
${var^^} Muestra el valor de la variable en mayúsculas [2]
${#var} Muestra la longitud de la variable (cantidad de caracteres)

[1] En la mayoría de los lenguajes de programación, las constantes se suelen escribir en mayúsculas.

[2] Estas acciones NO alteran los caracteres almacenados en la variable. Solamente los presentan de esa manera.

Extraer el nombre de un archivo con o sin su extensión

Ya vimos que con el comando basename se puede extraer el nombre del archivo con o sin su extensión. Ahora bien, basename es un programa en sí y es externo a Bash, pero de todas maneras podemos integrarlo fácilmente en nuestro código:

# Primero definimos una variable con la ruta del archivo
ruta="/home/usuario/documento.pdf"

# Extraer el nombre del archivo con su extensión
nom_ext=$(basename $ruta)
echo $nom_ext       # documento.pdf

# Extraer únicamente el nombre
nom=$(basename $ruta .pdf)
echo $nom           # documento

El método con basename tiene un par de desventajas, porque no solamente es más lento debido a que requiere ejecutar otro proceso, sino que también debemos conocer de antemano cuál la extensión del archivo.

Por suerte existen otros métodos llamados expansión de parámetros con los que podemos obtener los mismos resultados:

Comando Descripción
${ruta##*/} Extrae el nombre y la extensión del archivo
${ruta##*.} Extrae únicamente la extensión del archivo
${arch%.*} Extrae únicamente el nombre del archivo (arch debe tener tanto el nombre como la extensión)

El primero le indica a Bash que busque el patrón más largo (##) que termine en una barra (*/) y que lo elimine. Con el segundo es lo mismo, pero terminando en punto en lugar de una barra.

ruta="/home/usuario/documento.pdf"

# Nombre y extensión
nom_ext=${ruta##*/}
echo $nom_ext       # documento.pdf

# Sólo extensión
ext=${ruta##*.}
echo $ext           # pdf

Con el tercero necesitamos un paso extra, ya que no podemos aislar directamente el nombre del archivo sin su extensión. Además debemos tener especial cuidado porque existen algunos archivos con extensión doble, como por ejemplo los archivos .tar.gz; en estos casos debemos repetir el último paso.

ruta1="/home/usuario/documento.pdf"

# Primero quitamos la ruta...
nom_ext=${ruta1##*/}
# ...y luego extraemos sólo el nombre
nom=${nom_ext%.*}
echo $nom           # documento

# Con archivos con extensión doble, debemos repetir el último paso
ruta2="/home/usuario/comp.tar.gz"
arch=${ruta2%.*}    # comp.tar
nom=${arch%.*}      # comp
echo $nom           # comp

La desventaja de estos métodos es que requieren más pasos, pero la gran ventaja es que no es necesario saber cuál es la extensión.

Exportando variables

Podemos exportar una variable para que pueda ser utilizada por otro script. Esto se realiza primero con la palabra reservada export:

script1.sh

#!/bin/bash

nom1="Diego"
nom2="Ernesto"
nom3="Fernando"

echo "Script 1 => nom1 = $nom1, nom2 = $nom2, nom3 = $nom3"
export nom1 nom2      # Exporta únicamente nom1 y nom2

Y después la importamos en otro archivo con source o el operador de punto (.) más la ruta del archivo:

script2.sh

#!/bin/bash

. ./script1.sh        # Es lo mismo que source ./script1.sh
nom1="Alfredo"        # Aquí definimos otras variables pero con los mismos nombres
nom2="Benicio"

echo "Script 2 => nom1 = $nom1, nom1 = $nom2, nom3 = $nom3"

Luego, al ejecutar script1.sh:

Script 1 => nom1 = Diego, nom2 = Ernesto, nom3 = Fernando

Pero al ejecutar script2.sh:

Script 1 => nom1 = Diego, nom2 = Ernesto, nom3 = Fernando
Script 2 => nom1 = Alfredo, nom2 = Benicio, nom3 = Fernando

Como verás, script2.sh ha usado nuevamente el comando echo de script1.sh. Esto ocurre porque Bash no simplemente "lee" las variables, sino que copia todo el contenido y lo ejecuta línea por línea en el otro proceso.

Otra cosa muy importante a tener en cuenta es que tanto export como source funcionarán únicamente en la sesión actual.

Ejercicios

  1. Crear un ejecutable que le pida al usuario introducir su nombre y lo almacene en una variable, que guarde en otra variable la fecha de hoy y que imprima el mensaje “¡Hola, [nombre]! Hoy es [fecha]” mostrando respectivamente esos datos.
  2. Crear un ejecutable que le pida al usuario introducir una cadena de texto breve y que la imprima toda en mayúsculas, luego toda en minúsculas y que por último imprima la cantidad de caracteres que contiene.
  3. Crear un programa que le pida al usuario su nombre y su contraseña, imprimiendo "¡Hola, [nombre]! Ingresa tu contraseña: ".
  4. Crear primero el archivo imagen.jpg y luego utilizar ambos basename y la expansión de parámetros para mostrar por consola la ruta completa, el nombre del archivo con su extensión, sólo el nombre y sólo la extensión.

Pasando argumentos

Los archivos también pueden aceptar argumentos almacenados en variables cuyos nombres son $1, $2... $n. Estas variables almacenan los valores que se introducen en orden cuando se ejecuta el programa en la terminal. Por ejemplo, si un programa llamado saludo.sh tiene el siguiente código:

#!/bin/bash
echo Hola, $1 $2, ¿cómo estás?

Al ejecutarlo desde la consola con el comando ./saludo.sh Juan Pérez, siendo Juan el primer argumento que se almacena en $1 y Pérez en $2, el programa retornará:

Hola, Juan Pérez, ¿cómo estás?

Existen otras variables especiales que nos brindan información especial:

Variable especial Representa
$0 El nombre del archivo que se está ejecutando [3]
$n Accede al parámetro número n (del 1 al 9)
$# El número total de argumentos que se pasan al programa
$@ Todos los argumentos que se pasan al programa
$? La salida del último comando ejecutado
$$ El id del proceso que se está ejecutando (PID)
$! El PID del último comando ejecutado
$USER El usuario que está ejecutando el script
$HOSTNAME El nombre del equipo
$SECONDS La cantidad de segundos desde que comenzó la ejecución
$RANDOM Devuelve un número aleatorio
$LINENO El número de línea actual

[3] Por ejemplo, si se abre una terminal y se escribe echo $0, retorna bash.

Ejercicios

  1. Crear un programa que acepte varios argumentos e imprima por consola: "El nombre de este archivo es [nombre_archivo], admite [n] argumentos y sus valores actuales son [argumentos]."
  2. Crear otro programa que admita 4 argumentos y que muestre solamente el segundo y el tercero.
  3. Escribir un programa que muestre "Tu nombre de usuario es [usuario] y tu ID es [número_al_azar]".
  4. Escribir un programa que muestre el nombre del equipo, el PID actual, la cantidad de segundos transcurridos y el número de línea que se está ejecutando.