Contempo: Interpretar órdenes: Shell Script

Interpretar órdenes: Shell Script

Un script, es solamente una lista de instrucciones en un texto plano, eso. Es muy sencillo y fácil escribir un Shell script, no se necesitan grandes conocimientos para ello, solamente tener lógica y razonamiento a la hora de armar sus instrucciones para que luego se ejecuten. Como ya he mencionado en el tutorial de "Ver y grabar TV en Linux".

Lo primero es tener una idea para una orden, o sea; que es lo que quieren que haga (o cumpla). Lo segundo es escribir las ordenes, y lo último es ejecutar el script. Que en resumidas cuentas es: Idea, construcción, y ejecución. Como todo en la vida.

No hablaré sobre como construir scripts con ordenes complejas, no. Hablaré solo lo básico, y como crear scripts que cualquiera pudiera utilizar, además de dar ejemplos con ello. Para todo se utilizará el Shell de Unix: Bash. El Bash, es un interprete de ordenes que se utiliza comúnmente en las consolas/terminales de casi todas las distros de Linux que existen actualmente. Ahora, las manos a la masa...

Entrada y salida de comandos


Antes de llegar a la creación de un script, se necesitan conocimientos de como ejecutar ordenes en una terminal directamente. Que en realidad, es como crear un script, ya que todos estas ordenes se pueden agregar en un script sin problemas.

Un comando siempre se puede combinar con otro comando para formar una instrucción más elaborada, o tomar información de un archivo de texto a su conveniencia, todo eso explicado a continuación.

Entrada de comandos

En la construcción de una orden existen símbolos que se utilizan para una mejor lectura de la orden a la hora su ejecución. El símbolo '|' toma un comando de salida y lo convierte en la entrada de otro comando. Que sería como; el que va saliendo puede ayudar al que va entrando.

Daré un ejemplo.
Primero tendré que dar un ejemplo muy básico para que entiendan como se pueden después ingresar más ordenes de entrada. Si queremos que se muestre un texto exactamente igual al escrito en la terminal, se utilizará el comando 'echo' y las comillas con la oración entre medio de ellas:
echo "vuelan alas cortas"

Ahora, en vez de mostrar la oración "vuelan alas cortas" en una sola linea con el comando 'echo' que está saliendo, se agregará la entrada 'tr' después del símbolo '|' (es el que observa cuales son las ordenes que están saliendo, en este caso 'echo') para que entre esta nueva orden que es 'tr'. Y 'tr' irá acompañado de dos comillas sin nada entre medio (" "), y eso hará que se eliminen los espacios que existan en al oración. Y la entrada '/n' hará que cada palabra se salte hacia la otra linea (o que cada palabra ocupe una linea):
echo "vuelan alas cortas" | tr " " "\n"
vuelan
alas
cortas

Ahora harán exactamente lo mismo que el ejemplo de arriba, pero lo combinarán con un segundo símbolo '|', para que entre un nuevo comando a la orden. Este nuevo comando es la entrada 'sort', y mostrará cada palabra en orden alfabético:
echo "vuelan alas cortas" | tr " " "\n" | sort
alas
cortas
vuelan

También se pueden mezclar los comentarios de 'echo' con las comillas invertidas (`comando`), que al ejecutarse mostrará el resultado de la comando en vez del texto que se encuentre ahí, o sea que lo reemplazará. Por ejemplo, mezclen texto con una orden dentro de las comillas, esto mostrará el resultado al lado derecho de la oración "Directorio_actual:", que en este caso es 'pwd', para ver en que directorio se encuentran:
echo "Directorio_atual:`pwd`"

Esto serviría en el caso de un formulario de información también, se hará casi lo mismo, mezclar texto con comandos entre medio de las comillas invertidas (`comando`), y se saltará una línea por cada espacio que encuentre con la entrada 'tr " " "\n"':
echo "Usuario:$USERNAME Maquina:$HOSTNAME Directorio_de_usuario:$HOME Directorio_actual:`pwd` Archivos_txt:`ls *.txt`" | tr " " "\n"

La instrucción de ordenes del formulario mostrará lo siguiente (pero reemplazada por sus datos):
Usuario:<tu usuario>
Maquina:<nombre máquina>
Directorio_de_usuario:</home/tu_usuario>
Directorio_actual:</ruta/directorio/actual/>
Archivos_txt:<archivos.txt encontrados en el directorio>

Otro ejemplo de como emplear el símbolo '|', es ver el tamaño de un archivo mientras se copia, y que con el comando 'watch' (que es el de entrada) más unas opciones de ese comando junto con el comando 'ls' se puede observar el progreso de lo copiado del archivo:
cp /directorio/archivo /directorio_actual | watch -n 0 ls -sh archivo

Salida de comandos

Hablaré sobre el símbolo '>', que redirige la salida de un comando hacia un archivo (si el archivo no existe es creado automáticamente). La información que nos dé el comando 'date' será la fecha actual, eso se enviará al archivo.txt en forma de texto:
date > archivo.txt

El símbolo '>' reemplazará todo lo que se encuentre escrito en archivo.txt (borrará lo que se encuentre escrito ahí y escribirá lo nuevo), si quieren agregar información
a archivo.txt sin que se borra lo anterior, entonces utilizarán los simbolos '>>', ejemplo:
date >> archivo.txt

El símbolo '<' toma el contenido de un archivo y lo utiliza como entrada de un comando. Es parecido a '|', pero con este podemos utilizar instrucciones que se encuentren en textos, o simplemente datos que se encuentren en ellos para mostrarlos en pantalla.

El comando 'wc' cuenta las lineas, las palabras (o lo que este separado por espacios) y bytes del archivo en total (lineas palabras bytes), quiere decir que en el archivo.txt hay 2 lineas, 12 palabras, y 58 bytes, ejemplo:
wc < archivo.txt
2   12   58

Algo parecido sería utilizar el símbolo '|' para utilizar el comando de entrada 'wc' (con el comando 'cat' se lee lo que hay en el archivo.txt):
cat archivo.txt | wc
2   12   58

Sentencias de control


Las ordenes son primordiales a la hora de ejecutar un script, para eso hay que emplear su lenguaje, las sentencias de control. Es bien básico todo, no creo que se agobien con cinco ordenes.

La primera, la ejecución condicional 'if', que es la que parte la orden de instrucciones, y 'fi', que es la que la finaliza. Los demás son bucles o ciclos de control, como: for, while, until (ruptura de bucle: continue, break). Y la elección de opciones 'case'.

Las sentencias de control como: if, while, for, until, y case. Cuentan con expresiones para su correcta ejecución, como por ejemplo: "en el caso de...", "que pasaría sí...", "y si...", "o también...", cosas de ese estilo para emplearse en diferentes situaciones:

Uso general
  • ! expresión (La expresión es falsa)
  • expr1 -a expr2 (AND (español: y))
  • expr1 -o expr2 (OR (español: o))

Expresiones con ficheros
  • -e fichero (El fichero existe)
  • -f fichero (El fichero existe y es un fichero ordinario)
  • -d fichero (El fichero es un directorio)
  • -r fichero (El fichero tiene permiso de lectura)
  • -w fichero (El fichero tiene permiso de escritura)
  • -s fichero (El fichero tiene tamaño no nulo)

Comparación de cadenas
  • -z cadena (La cadena esta vacía)
  • -n cadena (La cadena no esta vacía)
  • cadena1 = cadena2 (Las cadenas son iguales)

Comparación de enteros
  • a -eq b (Los enteros son iguales)
  • a -ne b (Los enteros son distintos)
  • a -lt b (a es menor que b)
  • a -gt b (a es mayor que b)
  • a -le b (a es menor o igual que b)
  • a -ge b (a es mayor o igual que b)

Esqueleto de órdenes


Para crear un buen script tenemos que tener razonamiento, y poder crear un buen esqueleto de ordenes o sintaxis de control.

Sintaxis de if

Explicaré el ejemplo 3, ya que es el más ostentoso, y por ende se peude sacar por logica los otros dos ejemplos. Empieza así:

Si (if) orden1 se cumple
entonces (then)
orden2 se ejecuta
además si (elif)
orden3 se cumple
entonces (then)
orden4 se ejecuta
de otro modo (else)
orden5 se ejecutará
fin operación (fi)

Ejem. 1:      | Ejem. 2:      | Ejem. 3:
if orden1     | if orden 1    | if orden1
then            | then             | then
orden2        | orden2        | orden2
fi                 | else             | elif
                   | orden3        | orden3
                   | fi                 | then
                                        | orden4
                                        | else    
                                        | orden5
                                        | fi

Sintaxis de while

Mientras que se ejecuta orden1, si el resultado es verdadero, ejecuta orden2 y vuelve a ejecutar orden1. (dentro de orden2 se puede usar 'continue' para volver al comienzo del bucle y 'break' para salir de él).
while orden1
do
orden2
done

Sintaxis de for

Ejecuta "orden" tantas veces como palabras haya, la variable 'var' toma el valor de la palabra correspondiente.
for var in palabras
do
orden
done

Sintaxis de until

Ejecuta orden1. Si el resultado es falso, ejecuta orden2 y vuelve a ejecutar orden1.
until orden1
do
orden2
done

Sintaxis de case

Si hay varios patrones que coinciden, se ejecuta el conjunto de ordenes del primero que lo haga. Si no coincide con ninguno, no se ejecuta nada.
case palabra in patron1 | patron2
* )  orden ;;
esac

Creando un script e interpretar una orden


Utilizando ordenes básicas de script en un editor de texto, utilizarán la lógica de verdadero o falso (Si o No, en este caso).

Primer script

La orden es preguntar si existe (-e) el archivo.txt en el directorio en que se encuentra actualmente. Si existe archivo.txt lanzará un Si, si no existe lanzará un No.
#!/bin/bash
if [ -e archivo.txt ]
then
echo "Si"
else
echo "No"
fi

Siempre al guardar un script, ecribir en la primera linea '#!/bin/bash', para que el sistema reconozca que es un script para shell y vea el shell que se está utilizando.

Para guardar el script (en un archivo de texto), se puede utilizar cualquier nombre, pero ahora le pondrán "script" (o el que quieran). Ya guardado, se cambian los permisos de ejecución del archivo, y en una terminal tipear:
chmod +x script

Ejecutando el script en una terminal:
./script

Como mencioné arriba: Si existe "archivo.txt" lanzará un Si, si no existe lanzará un No.

Diferentes formas de escribir el mismo script

También el punto y coma ';' es una gran ayuda a la hora de ahorrar espacio en un script, pero no muy buena idea si se quiere un script fácil de leer al ojo. Como por ejemplo, se puede utilizar el mismo ejemplo de arriba:
if [ -e archivo.txt ]; then echo "Si"; else echo "No"; fi

El comando 'test' también se puede utilizar, ya que cumple la misma función que "[ -e archivo.txt ]" (y por que es un alias de '[ ]', o sea que cumple la misma función). El mismo ejemplo de arriba:
if test -e archivo.txt
then
echo "Si"
else
echo "No"
fi

Los scripts también se pueden interpretar directamente en la terminal, sin ninguna clase de fichero de texto de por medio, solamente se presiona la tecla 'Enter' al final de cada línea. Y también se pueden escribir con punto y coma (;) en una terminal, al igual que en un script, de la siguiente manera, con el mismo ejemplo de que hemos visto hasta ahora:
if [ -e archivo.txt ]; then echo "Si"; else echo "No"; fi

Y también en caso de utilizar el alias 'test':
if test -e archivo.txt; then echo "Si"; else echo "No"; fi

También se pueden utilizar condiciones de ejecución, como '&&' (AND, español: y), '||' (OR, español: o). Y por supuesto, con el mismo ejemplo que hemos visto:
[ -e archivo.txt ] && echo "Si" || echo "No"

Y también en caso de utilizar el alias 'test': 
test -e archivo.txt && echo "Si" || echo "No"

Como se ha visto hasta ahora, un script puede ser escrito de diferentes formas, pero dando al final el mismo resultado. Ya sea con la ayuda de un editor de texto para su futura utilización, o por medio de la terminal, el script tendrá el mismo funcionamiento y resultado.

Leer argumentos de ingreso de información


A la hora de leer un script y que la shell sepa diferenciar ordenes, se puede emplear el símbolo '$' cuando alguien ingresa información, y el script tome esa información y la ejecute.

Ingreso de información no numérica

Para interpretar solo números se utiliza: $#. Para interpretar todo tipo de argumentos se utiliza: $*. Si se cambia el signo #, a un valor numérico: $1, $2, $3. El valor numérico indicará el lugar (después de un espacio) en donde se interpreta un argumento en la terminal (sea cual sea: texto, numérico o caracteres).

El siguiente script mostrará solamente los primeros tres argumentos que se den en la terminal, mostrando un argumento por linea:
echo $1
echo $2
echo $3

El script también puede ser así:
echo $1; echo $2; echo $3

Si lo ejecutan en terminal, y más lo que están argumentando/escrito por ustedes en la terminal (la oración), mostraría el siguiente resultado:
./script Soy de Latinoamerica
Soy
de
Latinoamerica

Si escribieran más de tres argumentos, no mostraría el cuarto argumento (lo omitiría).

Ingreso de información numérica

Ahora utilizarán la orden 'expr', que solamente sirve para valores numéricos. La orden es que tome los primeros dos valores numéricos (separados por espacios) que escriban ustedes a la hora de ejecutarlo en la terminal, sume esos dos, y que les de el resultado final. El script es así:
echo "Los valores dados son:" $1 "y" $2
echo "El resultado de los valores es:" `expr $1 + $2`

Si lo ejecutan en terminal, y más los valores numéricos dados por ustedes:
./script 24 6
Los valores dados son: 24 y 6
El resultado de los valores es: 30

Si dieran un valor no numérico nos saldría: "expr: argumento no numérico".

Interactuar con el script: Pregunta y respuesta

La siguiente es una operación que interactua con el usuario, gracias al comando 'read' con dos variables (fnom y lape), y el símbolo '$'. La orden es que pregunte y espere la respuesta del usuario, para que finalmente de el resultado final de las respuestas:
echo "¿Tu nombre?"
read fnom
echo "¿Tu apellido?"
read lape
echo "Hola $fnom $lape"

Si lo ejecutan en terminal se vería así:
./script
¿Tu nombre?
<aqui escriben su nombre> (y tecla Enter)
¿Tu apellido?
<aqui escriben su apellido> (y tecla Enter)
Hola <su_nombre> <su_apellido>

Algo similar a lo anterior, sería que en una sola línea escribieran el nombre y apellido, solo hay que juntar las variables fnom y lape, o el nombre de las variables que quieran. Así quedaría el script:
echo "¿Tu nombre y apellido?"
read fnom lape
echo "Hola $fnom $lape"

Utilizarlas con sentencias de control

Ahora harán un 'if' que lleve lo que hemos visto hasta ahora. Al ejecutarse tendrán que ingresar un número (la variable numvar es la que leerá la información ingresada), si no es un 7, nos pedirá ingresar un siete, si ingresamos un 7, nos lanzará un mensaje positivo.
echo -n "Ingresa un número: "
read numvar
if [ "$numvar" = "7" ]; then
echo "¡Por fin ingresaste un siete!"
else
echo "Por favor ingresa el número siete."
fi

Ahora crearán un script que contiene dos 'if', y que integre lo que se ha visto hasta el momento. Les pedirá ingresar un número menor que 3, si se ingresa un 1 o un 2 lanzará un mensaje positivo, si se ingresa un número igual o mayor que 3, lanzará un mensaje negativo.
echo -n "Ingresa un número menor que 3: "
read numvar
if [ "$numvar" = "1" ]
then
echo "Ingresaste un uno."
else
if [ "$numvar" = "2" ]
then
echo "Ingresaste un dos."
else
echo "No ingresaste un número menor que 3."
fi
fi

En este caso, también se puede emplear 'case' en vez de utilizar 'if'. La orden será la misma que el ejemplo anterior:
echo -n "Ingresa un número menor que 3: "
read numvar
case $numvar in
1 ) echo "Ingresaste un uno."
;;
2 ) echo "Ingresaste un dos."
;;
* ) echo "No ingresaste un número menor que 3."
esac

Y así es como seguiría por días dando ejemplos, en que practicamente es casi lo mismo, pero solo cambiarian las ordenes y las sentencias de control. Así que a partir de aquí ya es asunto de ustedes cuanto pueden interactuar, y como podrían contruir sus propios scripts gracias a esta base de conocimiento, que nunca está demás seguir leyendo guías de Shell script.