Aso Ud01 Scripting Con Bash Alum

Descargar como odt, pdf o txt
Descargar como odt, pdf o txt
Está en la página 1de 86

UD 01: Scripting con bash

1.- El shell Bash


Uno de los aspectos que caracteriza un sistema operativo de tipo Unix es el
hecho de que es multiusuario, es decir, permite el uso del mismo ordenador a más de
un usuario al mismo tiempo (por ejemplo, a través de ssh). 

/usr/local/bin → administradores (scripts para toda la comunidad)


/home/usuario/bin → pruebas, scripts propios
Se añade en estas dos localizaciones ya que se encuentran en la variable path.
$ls → ¿bin?. No esta

$mkdir bin

$cat.profile
ANTONIO J. LEÓN DELGADO
1
UD 01: Scripting con bash
Probar ahora el echo $path y ver si esta /home/raul/bin (reiniciar)

2.- Crear el script: cls.sh

$sudo nano cls.sh


(una vez dentro)
#!/bin/bash
clear
(ctrl+f2 o f3)

3.- ejecutamos el script


$cls.sh
$sudo cls.sh → no tiene permisos

Le damos permisos
$sudo chmod 755 cls.sh
$cls.sh
$ls -l

Para cada sesión de usuario, el sistema genera un shell propio para él que


actúa como intérprete interactivo de las órdenes que el usuario ejecuta, lo que
permite que los usuarios puedan elegir un shell u otro de acuerdo con sus
necesidades. 
El hecho de que el shell esté separado del resto del sistema operativo
proporciona flexibilidad, porque permite seleccionar la interfaz más adecuada a las
necesidades de cada usuario.
El archivo /etc/shells da información sobre los shells válidos para iniciar sesión
en un sistema Unix determinado. Que sean válidos no significa que estén instalados.
Si se quieren utilizar, hay que tenerlos instalados en el sistema.
Para instalar ksh:
sudo apt-get install ksh

ANTONIO J. LEÓN DELGADO


2
UD 01: Scripting con bash
¿Shells válidos?

¿Qué es /bin/sh?
Es un ejecutable que representa el shell del sistema
Enlace simbolico a dash, ,es decir, si ejecutamos sh, realmente estamos
ejecutando dash

Realmente es el intérprete /bin/dash

El shell que se ejecuta por defecto cuando un usuario inicia sesión queda


definido en el archivo /etc/passwd, en el último campo de la línea correspondiente al
usuario.

Ejemplo de línea de un fichero /etc/passwd


La siguiente es una línea de una cuenta llamada "mia" del archivo /etc/passwd.
 En el último campo está escrito /bin/bash, lo que indica el shell por defecto de la
cuenta:
mia:x:1011:1000:Mia Maya:/home/mia:/bin/bash
Normalmente, el administrador del sistema es el encargado de fijar el shell de
inicio de sesión de los usuarios. En algunos entornos, está habilitado el uso de órdenes
como chsh para permitir a los usuarios cambiar su shell por defecto de inicio de sesión.
Ejemplo de utilización del programa /usr/bin/chsh

Imaginemos que "mia" está trabajando en un entorno en el que se le permite


hacer el cambio de su shell de inicio y que el shell /bin/tcsh está instalado en el
sistema. La cuenta mia tiene como shell de inicio el bash y tiene la necesidad de
cambiarlo por tcsh. Por ello ejecuta la siguiente orden:
$chsh -s / bin/tcsh
El resultado de la orden anterior es un cambio en la línea del archivo
/etc/passwd correspondiente a la cuenta "mia". En el último campo ahora habrá
escrito /bin/tcsh ya partir de ese momento todas las sesiones nuevas ejecutarán
este shell por defecto.
mia:x:1011:1000:Mia Maya:/home/mia:/bin/tcsh
Otro ejemplo
ANTONIO J. LEÓN DELGADO
3
UD 01: Scripting con bash
$chsh -s /bin/dash
Resultado:

Observación: si hemos iniciado sesión como usuarios con un shell determinado,


en la misma sesión podemos arrancar un nuevo shell.

A este le llamamos subshell o shell hijo, que puede ser del mismo tipo o de uno
diferente disponible en el sistema. Para iniciar un subshell sencillamente tenemos que
escribir el nombre del programa (sh, bash, csh, ksh, etc.) en la línea de comandos y
pulsar la tecla de retorno. Si hemos cambiado de shell, en general, aparecerá un
indicador de línea de comandos diferente, ya que cada shell tiene una apariencia
diferente. Podemos finalizar la ejecución del subshell y devolver al shell padre
escribiendo la orden exit.

Comandos internos y externos


Un comando eterno es un archivo localizado en el sistema (arbol del sistema).
Se considera caomandos externosa:
archivos con formaro binario ejecutable. Por ejemplo /bin/chmod.
Archivos con formato de texto que representan un script de comandos
(excritos en bash, python, perl, etc.).

El comando file indica el tipo de daros contenidos en el archivo


($ file/bin/chamod)

($ file Escritorio/scripssts/grep_raul.sh)

el argumento del comado file es un nombre

Un comando interno se integra en la shell y por tanto no se corresponde en


ningun caso con un archivo almacenado en el disco
el comando type indica si unn comando riene o no implementacion interna.
En la imahen sisguiente observamos que el comado cd es interno y el comado chamod
es externo

El comando type tiene como argumento el nombre de un comando. Si este


ultimo no es interno, se busca dentro de ka cariable path. Mas sobre tupe en:

Un comando interno se ejecuta con el shell actual

Implementacion interna y externa

ANTONIO J. LEÓN DELGADO


4
UD 01: Scripting con bash
Ciertos comandos tienen una imlementacion interna y una externa. En este aso el
coamndo interno riene preferencia sobre el externo siendo ademas si ejecucion mas
rapida. Enla siguiente imagen vemoss que el comando pwd tiene las dos
implementaciones):
(type -a pwd)

Dos usos de pwd interno y externo respectivamente

Vemos que linkscripts es un enlace simbólico a un directorio.


El comando interno pwd muestra el nombre del enlace como si linkscripts fuera un
directorio mientras que el comando externo muestra el nombre real del directorio

El shell Bash
El shell Bash es el shell predeterminado de casi todas las distribuciones
GNU/Linux, así como de Mac OS X, y puede ejecutarse en la mayoría de los sistemas
operativos tipo Unix. También se ha llevado a Microsoft Windows para el proyecto
Cygwin.

El nombre Bash
Es un acrónimo de Bourne-Again Shell (“otro shell Bourne"), haciendo un juego
de palabras (born-again significa "renacimiento") sobre el shell Bourne (sh), que fue
uno de los primeros intérpretes importantes de Unix.
El Bash es un intérprete de comandos compatible con el shell Bourne (sh), que
incorpora características útiles del shell Korn (ksh) y del shell C (csh), con el objetivo de
cumplir con el estándar POSIX. Bash ofrece mejoras funcionales sobre sh tanto para la
programación como para el uso interactivo y se ha convertido en el estándar de facto
para la programación de guiones de shell.
Observación: como el shell Bash (bash) es compatible con el shell Bourne (sh),
las órdenes y los programas escritos para sh pueden ser ejecutados con bash sin
modificación, pero lo contrario no siempre es cierto.
Abrir una sesión con Bash
Cuando arranca, el sistema presenta a los usuarios una interfaz determinada
que pu ede ser de texto o gráfica, dependiendo de los sistemas operativos o de cómo
esté configurado el modo de arranque.
La interacción con Bash se hace a través de un emulador de terminal. Si hemos
iniciado sesión al sistema en modo consola (texto), una vez validados obtendremos el
ANTONIO J. LEÓN DELGADO
5
UD 01: Scripting con bash
acceso directo a un shell. Si hemos iniciado sesión en modo gráfico, tendremos que
ejecutar alguno de los programas de emulación de terminal disponibles. La mayoría
de los sistemas Unix disponen de una aplicación de terminal accesible por alguna de
las opciones del menú principal, por ejemplo, la distribución de Linux Debian, por
ejemplo, Ubuntu, accedemos a un terminal desde 
Aplicaciones > Accesorios > Terminal (depende de la versión y el escritorio
instalado).

Otro caso de obtención de un shell interactivo es el acceso remoto a la


máquina, tanto por cualquiera de las posibilidades de texto (Telnet, rlogin, ssh) como
de forma gráfica.
Sea cual sea el modo de acceso, al iniciar sesión el sistema genera
un shell para nosotros que hará de intérprete de las órdenes que ejecutamos en
aquella sesión. Para ello el shell hace lo siguiente:
1.- Muestra por pantalla el indicador de la línea de comandos (el prompt)
señalando que está listo para aceptar órdenes.
2.- Cuando el usuario introduce una orden, el shell la interpreta (busca las
órdenes, hace la expansión de nombres de ficheros, sustituye los valores de variables
para variables referenciadas, etc.).
3.- Si encuentra algún error muestra al usuario un mensaje de error.
4.- Si la orden está bien escrita, entonces localiza el programa a ejecutar y
pide al sistema operativo que la ejecute, pasando a él el control.
5.- Cuando finaliza la ejecución del programa, el control retorna al shell, que
vuelve a mostrar el prompt esperando una nueva orden.
Después de iniciar sesión, podemos comprobar cuál es el shell que tenemos
establecido por defecto y con el que estamos trabajando ejecutando la siguiente
orden:
$echo $SHELL
Si queremmis saber la version de bash:
$echo $BASH_VERSION

ANTONIO J. LEÓN DELGADO


6
UD 01: Scripting con bash

Interpretación de órdenes
La función principal del shell es interpretar órdenes, ya sea las órdenes escritas
de manera interactiva en la interfaz de la línea de comandos que proporciona, o las
órdenes escritas en un archivo de texto (guion de shell).
Las órdenes que escribimos, ya sea en el indicador de la línea de comandos
del shell o en un programa de shell (por ejemplo, terminator), tienen el siguiente
formato:
nom_orden [ -opciones ] [ argumentos ] <INTRO>, donde:
nom_orden: es el nombre de la orden que queremos ejecutar.
opciones: las órdenes pueden o no llevar opciones. Normalmente las opciones
se escriben con un guion delante o con doble guion seguido de una palabra.
argumentos: dependiendo de la orden, se pueden poner argumentos que
muchas veces representan una cadena de caracteres, el nombre de un archivo o
directorio.
El shell interpreta siempre el espacio en blanco como separador de órdenes,
opciones o argumentos. Si no ponemos los espacios correctamente, obtendremos un
mensaje de error.

Ejemplo: $ls –l /home


ls  comando
-l  opción
/home  argumento

ANTONIO J. LEÓN DELGADO


7
UD 01: Scripting con bash
Ejemplo de ejecución de órdenes
Es posible que una misma orden acepte diferentes modos de ejecución: sin
argumentos, con opciones, con argumentos. Por ejemplo, el comando ls:
ls
ls -l
ls /etc/shells
ls -l /etc/shells
La orden siguiente da error porque no hemos puesto el espacio requerido para
separar la opción y el argumento:
ls-l /etc/shells

Expansión de nombres de ficheros


El shell nos proporciona una característica que se llama generación de nombres
de ficheros o expansión de nombres de ficheros, que nos ahorra tiempo a la hora de
teclear los nombres de los archivos con los que operan las órdenes.
La generación de nombres de ficheros permite utilizar unos caracteres
especiales para especificar grupos de nombres de archivos. Se pueden encontrar
nombres de archivos o directorios que cumplen un patrón determinado, por ejemplo,
todos los nombres que terminan en .c, o todos los que empiezan por test, o todos los
que tienen tres caracteres. Mediante el uso de las expresiones regulares, podemos
referirnos a los nombres de los ficheros que cumplen el patrón determinado. 
El shell expande el patrón correspondiente a los nombres de archivos antes de
ejecutar la orden. La siguiente tabla muestra las expresiones regulares más utilizadas
para la generación de nombres de archivos

Ejercicio 1º
Ejemplos de generación de nombres de ficheros con expresiones regulares
Sitúate en el directorio /usr/bin:
Lista los nombres de archivos y directorios que empiecen por c:
LS
Find

ANTONIO J. LEÓN DELGADO


8
UD 01: Scripting con bash

ANTONIO J. LEÓN DELGADO


9
UD 01: Scripting con bash
Los que empiecen por la letra y ó z:
LS

FIND

Los que terminen en .sh:


LS

FIND

Los que empiecen por r y terminen en e:


LS

FIND

ANTONIO J. LEÓN DELGADO


10
UD 01: Scripting con bash

ANTONIO J. LEÓN DELGADO


11
UD 01: Scripting con bash
Los que empiecen por alguna letra comprendida entre la a y la d:

Todos aquellos ficheros cuyo nombre tenga cuatro caracteres:

ANTONIO J. LEÓN DELGADO


12
UD 01: Scripting con bash
Ejercicio 2º
Crea , en un tu direcctorio de conexión, el directorio regex y despues, los siguiente
ficheros: f12, f1.i, fICa, fic.c, fic.s, miscript.p1, MISCRIPT.p1 y oso.c

1º Mostrar todos los nombres de ficheros que terminen en .c

2ºMostrar todos los nombres de ficheros que comiences por la letras f

3º Mostrar todos los nombres de ficheros que tengan una extensión compuesta de
un solo carácter

4º Mostrar todos los nombres de ficheros cuyo nombre empiece por f u o y termine
con el carácter . Seguido de un minúscula

5º Mostrar todos los nombres de ficheros cuyo nombre tiene en el 2º carácter una
mayúscula, un dígito o la letra i. A continuación, puede ir una cadena cualquiera,
incluso nada.

6ºMostrar todos los nombres de ficheros que no comiencen por minuscula

7ºEliminar, pidiendo confirmación, todos los ficheros cuyo nombre termine en .c o en


.s

ANTONIO J. LEÓN DELGADO


13
UD 01: Scripting con bash
Contamos con otras expresiones que se utilizan tambien para generar nombres de
ficheros, comparar cadenas de caracteres,etc:

Tenemos estas:
¿(expresion)
La expresion estara presente 0 o 1 veces
*(expresion)
La expresion estara presente 0 o n veces
+(expresion)
La expresion estara presente 1 o n veces
@(expresion)
La expresion estara presente solo 1 vez
!(expresion)
La expresion o estara presente
Alternativas
Una barra vertical en el interuir de una expresion compleja significa “o bien”.
?(expresion|expresion|...)
*(expresion|expresion|...)
+(expresion|expresion|...)
@(expresion|expresion|...)
!(expresion|expresion|...)

Vamos a crear el directorio regex_complejas donde crearemos los siguientes


ficheros:
sudo touch fic,fic.log,fic866.log,fic866866.log,fic866868.log,fic867.log,
fic868.log,readme.txt,typescript.

Observación: [^] equivale a [!]


Expresiones regulares
En informática, una expresión regular es una representación, según unas reglas
sintácticas de un lenguaje formal, de una porción de texto genérico a buscar dentro de
otro texto, como por ejemplo unos caracteres, palabras o patrones de texto
concretos. El texto genérico de la expresión regular puede representar patrones con
determinados caracteres que tienen un significado especial, como el carácter
interrogante (?) para representar un carácter cualquiera; el carácter comodín, *, para
representar un número cualquiera de caracteres, etc.
Observación: las expresiones regulares de generación de nombres de ficheros
no generan nombres de ficheros que empiezan por punto (el punto siempre se
indicará explícitamente).

Variables del shell

ANTONIO J. LEÓN DELGADO


14
UD 01: Scripting con bash
En shell, una variable es un nombre que representa un valor. La sintaxis para
definir y asignar un valor a una variable es:
nombre_variable=valor

Observación: ten en cuenta que no hay ningún espacio antes ni después del
signo igual. Si se pone algún obtendremos un error.
Los siguientes son ejemplos válidos de definición de variables:
NOMBRE=Marco
APELLIDOS ="Ros Rojo"
EDAD=31
El valor asignado a la variable APELLIDOS la hemos cerrado entre comillas
dobles para que el shell no interprete el espacio que hay entre los dos apellidos. Para
evitar la posibilidad de cometer errores, podemos optar por poner el valor siempre
entre comillas dobles:
variable = "valor"
El valor de la variable siempre lo podemos cambiar asignándole otro valor (es
decir, es como si eliminamos la variable y la volvemos a crear con un nuevo valor). Por
ejemplo:
NOMBRE=mia
El valor asociado a una variable puede ser utilizado mediante el nombre de la
variable precedido con el símbolo dólar:  $variable.
Este mecanismo se llama sustitución de variables.
El shell realiza la sustitución de variables a cualquier línea de comandos que
contenga un símbolo $ seguido de un nombre de variable válido.
Podemos visualizar el valor de una variable definida con el orden echo y
utilizando el mecanismo de sustitución de variables:
echo $variable
Por ejemplo:
echo $APELLIDOS
Después de ejecutar la orden anterior, el valor asociado a la variable llamada
APELLIDOS se muestra por pantalla.
En la sustitución de variables se puede hacer uso de llaves para delimitar el
nombre de la variable: ${variable}. La utilización de llaves nos permite, por ejemplo,
hacer la sustitución de la variable seguida de un texto. Por ejemplo:
PREFIJO=extra
echo ${PREFIJO}ordinario
La orden anterior muestra por pantalla la palabra "extraordinario". Haciendo
uso de la misma variable PREFIJO podemos escribir la siguiente orden:
echo "Palabras que empiezan por $PREFIJO: ${PREFIJO}ordinario, ${PREFIJO}polar”
La orden anterior muestra por pantalla la frase "Palabras que empiezan por
extra: extraordinario, extrapolar".

Por convención, los nombres de las variables se escriben en mayúsculas, pero


se pueden poner en minúsculas. Los nombres de las variables deben empezar
ANTONIO J. LEÓN DELGADO
15
UD 01: Scripting con bash
obligatoriamente por carácter alfabético (az, AZ) y pueden contener caracteres
alfabéticos, numéricos y subrayados. No hay restricciones respecto al número de
caracteres que puede tener el nombre de una variable.
Las variables pueden ser variables locales o variables de entorno:
Variables locales: sólo son visibles por el shell en el que estamos trabajando, no
son visibles por ningún shell hijo.
Variables de entorno: son visibles tanto por shell padre como por
los shells hijos. Al ser creado, el shell hijo "hereda" las variables de entorno del padre
(recibe una copia). El shell hijo puede cambiar las variables heredadas del shell padre,
pero, al ser una copia, los cambios hechos en el hijo no afectan al padre.
Una variable definida en un proceso hijo, ya sea local o de entorno, no es
visible por su proceso padre.
La manera de hacer una variable, variable de entorno, es mediante la
orden export:
nombre_var = valor
export nombre_var
La asignación y exportación se puede hacer en un solo paso:
export nombre_var = valor
Ejemplo de manipulación de variables locales y de entorno
Ejecuta de manera secuencial las líneas de comandos siguientes:
X=11 # Definimos una variable x con valor 11
echo $X # Mostramos el valor de x
bash # Abrimos un shell hijo
echo $X # El shell hijo no conoce el valor de x
exit # Salimos del shell hijo y regresamos al padre
export X # Exportamos la variable x en el entorno
bash # Abrimos un shell hijo
echo $X # Ahora el shell hijo sí conoce el valor de x
X=12 # Modificamos en el shell hijo el valor de x
exit # Volvemos al shell padre
echo $X # El valor de x sigue siendo 11

Observación: si desde un script llamamos a otro script, este se ejecutará en un


shell hijo
Hay varias órdenes relacionadas con las variables:
Orden set: permite ver todas las variables (locales y de entorno) definidas en
una sesión.
Orden env: permite ver las variables de entorno definidas en una sesión.
Orden unset nom_variable: elimina la variable y el valor asociado a la variable.

ANTONIO J. LEÓN DELGADO


16
UD 01: Scripting con bash
Situaciones de scripts “hijos y padres”
1.- Creamos uno.sh y dos.sh

Ejecución de uno.sh

El shell hijo no conoce la variable x del shell padre ni el shell padre conoce la
variable x del shell hijo.

2.- Modificamos uno.sh para exportar la variable x

Ejecución de uno.sh

Ahora, el shell hijo conoce la variable x del shell padre (por el export) pero el
padre no conoce la variable del shell hijo.

3.- Elimino el export x de uno.sh y ejecuto con source dos.sh

ANTONIO J. LEÓN DELGADO


17
UD 01: Scripting con bash

Ejecuto uno.sh

Con source no creamos shells hijos.


En cualquier sesión de shell Bash están presentes una serie de variables de
entorno predefinidas por el sistema que nos pueden resultar de mucha utilidad en la
programación de guiones de shell, ya que guardan información genérica del entorno
de los usuarios.
La siguiente tabla muestra el nombre y la descripción de algunas de estas
variables (Enlaces interesantes relacionados)
 https://www.digitalocean.com/community/tutorials/how-to-read-and-set-
environmental-and-shell-variables-on-linux-es
 https://conpilar.es/como-personalizar-la-variable-de-shell-ps1-ps4-en-el-
indicador-bash/

Observación: localización

ANTONIO J. LEÓN DELGADO


18
UD 01: Scripting con bash
Localización se refiere al conjunto de convenciones que se utilizan en una zona
geográfica o cultural determinada, como el formato de la fecha y la hora, el nombre de
la moneda, el separador para decenas y centenas, etc. La orden locale muestra
información sobre el valor de las variables de localización del sistema.

Ejemplo de uso de variables de entorno predefinidas


Haciendo uso de la sustitución de variables y utilizando variables de entorno
predefinidas escriba una línea de comandos que al ejecutarse muestre por pantalla el
texto:
"Soy nombre_usuario y mi directorio de trabajo es directorio".
Los valores de nombre_usuario y directorio deben obtenerse de las variables
adecuadas y predefinidas por el sistema.
echo "Soy $USER y mi directorio de trabajo es $HOME "
Hacer lo mismo, pero ahora debe mostrar:
"Trabajo con un tipo de sistema sistema_operativo y un shell nombre_shell"
echo "Trabajo con un tipo de sistema $OSTYPE y un shell $SHELL "
Probar ${USER}, ${HOME}, ${UID}, etc.

ANTONIO J. LEÓN DELGADO


19
UD 01: Scripting con bash

Sustitución de órdenes o comandos


La sustitución de comandos se utiliza para reemplazar la salida de la ejecución
de una orden dentro de la misma línea de comandos.
Se puede hacer con la siguiente sintaxis:
1.- $(orden)
2.- Poniendo la orden entre acentos graves (tecla del circunflejo): `orden`.
Observación: las dos formas son válidas, pero si estamos escribiendo un shell
script es mejor elegir una y utilizar la misma manera a lo largo de todo el programa.
Del mismo modo que en la sustitución de variables, la sustitución de órdenes se hace
siempre antes de ejecutar la línea de comandos. Cuando el shell encuentra en la línea
de comandos un $ seguido de un paréntesis abierto, ejecuta todo lo que encuentra
hasta llegar al paréntesis cerrado. El resultado lo pone en la línea de comandos en el
lugar donde estaba el orden que sustituía.

Ejemplo de sustitución de comandos


Haciendo uso de la sustitución de comandos escribe una línea de comandos
que al ejecutarse la muestre por pantalla el texto: "La fecha del sistema es: fecha". El
valor de fecha se debe obtener del resultado de ejecutar la orden adecuada.
echo "La fecha del sistema es: $(date)"
También se podría haber hecho así: 
echo “La fecha del sistema es: `date`”.
La sustitución de órdenes es un mecanismo muy utilizado en la programación
de guiones de shell. Muchas veces se utiliza en la asignación de valores a variables. Por
ejemplo:
ANTONIO J. LEÓN DELGADO
20
UD 01: Scripting con bash
La orden date admite varios modificadores de formato de salida que se indican
con +%. Por ejemplo, date +%x indica la fecha del sistema con formato dd/mm/aa.
$HORA = $(date +%x)
$echo $HORA

Observación: si la salida de la orden que sustituimos contiene saltos de línea,


el shell los sustituirá por espacios en blanco. 
Por ejemplo, ejecuta lo siguiente:
$seq 10
La orden seq es utilizada para generar secuencias de números (para más
información ver man seq).
La orden anterior muestra por pantalla los números del 1 al 10 separado por
saltos de línea. Ahora introduce la misma orden en una sustitución de comandos, por
ejemplo:
$echo $(seq 10)
El resultado de la orden anterior es la lista de números separados por espacios.
Ejemplo de uso de variables y sustitución de órdenes
Asignar a una variable la lista de números pares que hay entre el 0 y el 20
(incluidos) separados por espacios:
$PARES = $(seq 0 2 20)
$echo $PARES

Expansión aritmética
El mecanismo de expansión aritmética del Bash permite la evaluación de una
expresión aritmética y la sustitución del resultado.
El formato para hacer la expansión aritmética es:
$((expresión_aritmética))

Observación: No confundir el mecanismo de sustitución de órdenes, $(orden)


con el de expansión aritmética, $((expresión)).
Ejemplo

La evaluación de expresiones aritméticas sólo admite números enteros. Los


operadores admitidos son casi los mismos que en el lenguaje de programación C.
La siguiente tabla muestra la lista de operadores existentes (no incluimos los que

ANTONIO J. LEÓN DELGADO


21
UD 01: Scripting con bash
operan con bits) en orden decreciente de precedencia. Podemos utilizar paréntesis
para alterar el orden de precedencia de los operadores.
var++, var-- Postincremento
++var, --var Preincremento

Observación: en las expresiones se admiten tanto variables del shell como


operandos y podemos utilizarlas haciendo la expansión de la variable $nombre_var, o
bien sólo con su nombre, nombre_var.
 Por ejemplo, dadas dos variables con los valores siguientes:
X=2
Y=3
La expresión siguiente:
Z=$((X + Y))
También la podemos escribir así: Z=$(($X + $Y))
El mecanismo de expansión aritmética nos permite realizar operaciones y
sustituir el resultado en otra orden, por ejemplo:
echo "El resultado de sumar $X y $Y es: $((X + Y))"
En el ejemplo anterior usamos la orden echo y en el argumento utilizamos la
expansión aritmética directamente para visualizar el resultado de la operación X + Y.

Tratamiento de los caracteres especiales


Los caracteres especiales son aquellos que tienen algún significado concreto
para el shell, por ejemplo:
1.- El carácter espacio indica separación de órdenes, opciones o argumentos.
2.- El carácter salto de línea indica final de orden.
3.- El carácter $ delante de un nombre indica referenciar el valor de una
variable con ese nombre.
4.- El carácter * es utilizado como expresión regular en la generación de
nombres de archivos.
5.- Las comillas.

ANTONIO J. LEÓN DELGADO


22
UD 01: Scripting con bash
Cuando no queremos que el shell interprete estos caracteres de manera
especial sino como caracteres normales, podemos anular su significado de las
siguientes formas:
1.- Precediendo el carácter del símbolo \. Esta técnica anula el significado
especial del carácter que va detrás.
2.- Con comillas dobles, "". Esta técnica anula el significado de todos los
caracteres especiales que estén dentro de las comillas dobles excepto el carácter
especial dólar, $, la barra inversa, \, el acento grave, `, y las comillas dobles, " ".
3.- Con comillas simples, ‘ ‘ (en el teclado es la tecla ? arriba y ‘ abajo).

Esta técnica anula el significado de todos los caracteres especiales que estén
dentro de las comillas simples.

Ejemplos de anulación del significado de los caracteres especiales


1.- Escribir una orden que muestre una frase como la siguiente:
Estoy leyendo el libro "Correr o morir". 
El título "Correr o morir" debe aparecer entre comillas dobles.
echo Estoy leyendo el libro \"Correr o morir \".

Se puede conseguir lo mismo con las comillas simples:


echo 'Estoy leyendo el libro "Correr o morir".'

2.- Escribir una orden que muestre la frase siguiente:


El contenido de $USER es nombre_usuario.
Se debe sustituir nombre_usuario por el valor de la variable USER.
echo “El contenido de \$USER es $USER”

ANTONIO J. LEÓN DELGADO


23
UD 01: Scripting con bash

Redirección de la entrada y la salida


En Unix los dispositivos de entrada y salida (E/S) y los archivos se tratan de la
misma manera y el shell los trata a todos como archivos. Todos los programas
ejecutados mediante un shell incluyen tres archivos predefinidos, especificados por
descriptores de ficheros (file handles) correspondientes. Por defecto, estos archivos
son los siguientes:
1.- Entrada estándar (standard input). Normalmente está asignada al
teclado. Utiliza el descriptor número 0.
2.- Salida estándar (standard output). Normalmente está asignada a la
pantalla. Utiliza el descriptor 1.
3.- Salida estándar de errores (standard error). Normalmente asignada a la
pantalla. Utiliza el descriptor 2.
Observación: esto nos indica que, por defecto, cualquier programa ejecutado
desde el Shell tendrá la entrada asociada al teclado, su salida en la pantalla y, si se
producen errores también los enviará a la pantalla.
Una característica que nos proporciona el shell es poder redirigir la entrada o la
salida estándares de una orden, es decir, nos permite hacer que una orden reciba su
entrada o envíe su salida desde o hacia otros ficheros o dispositivos, de modo que:
1.- La redirección de entrada permite que las órdenes cojan los datos de un
fichero en lugar de desde el teclado (<)
2.- La redirección de salida nos permite enviar la salida de un comando a un
archivo en lugar de en la pantalla. (>)
3.- La redirección de la salida de errores nos permite enviar la salida de
errores de una orden a un fichero en lugar de en la pantalla. (2>)
La siguiente tabla muestra los caracteres utilizados para redirigir la entrada y la
salida de las órdenes.

ANTONIO J. LEÓN DELGADO


24
UD 01: Scripting con bash

Si queremos que la ejecución de una orden no genere actividad por pantalla


(ejecución silenciosa), debemos redirigir todas sus salidas a /dev/null. Por ejemplo:
$ls /tmp/kk >& /dev/null
Si queremos redirigir la salida estándar y la salida de errores a un fichero con la
doble redirección, para que si el archivo no existe el cree y que si ya existe añada las
dos salidas, podemos hacerlo así: $ls /tmp /kk >> registro.log 2>> registro.log
Tuberías o 'pipes'
El shell permite enlazar la salida de una orden como entrada de otra orden
mediante lo que llamamos tuberías o pipes.
La sintaxis es la siguiente:
orden1 | orden2
La salida de la orden1 se utiliza como entrada de la orden2. El símbolo | se
llama pipe ("tubería" en inglés). Se pueden poner espacios entre la tubería y las
órdenes para hacer más legible la línea de comandos, pero no es obligatorio. Los
espacios son opcionales.
Por ejemplo, ejecutamos una orden echo para mostrar por pantalla una línea
de texto: $echo “Alba: Gómez: 08004: BCN”
La orden anterior muestra por pantalla una serie de datos separados por dos
puntos así: "Nombre: Apellidos: CP: Ciudad". Si de esta salida sólo queremos tomar el
tercer campo (el de código postal), la podemos redirigir con una tubería hacia el
orden cut para que seleccione únicamente el campo que nos interesa de la manera
siguiente:
$echo “Alba: Gómez: 08004: BCN” cut -d: -f3
En la figura siguiente podemos ver este ejemplo de manera gráfica. La salida del
comando echo pasa por la tubería y la recibe como entrada la orden cut, la cual la
procesa y muestra por pantalla la salida procesada.

ANTONIO J. LEÓN DELGADO


25
UD 01: Scripting con bash

Vemos otro ejemplo sencillo de utilización de tuberías:


cat /etc/passwd | more
En el ejemplo anterior la salida del comando cat /etc/passwd se utiliza como
entrada del orden more para ver el contenido del fichero /etc/passwd de forma
paginada, es decir, haciendo una pausa cada vez que se llena la pantalla.

Observación: con la orden more avanzamos de página con la tecla de espacio.


Una línea de comandos escrita con una tubería o pipe se conoce como
pipeline. Las tuberías no están limitadas sólo a dos órdenes. Se pueden
hacer pipelines más largos asociando la salida de una orden con la entrada de la
siguiente y así sucesivamente.
orden1 | orden2 | orden3 | orden4 ...
Por ejemplo:
$cat /etc/passwd | sort | more
En el ejemplo anterior hay un encadenamiento de tres órdenes con transmisión
de sus datos de una a la otra: la salida de cat envía de entrada a sort y la salida de sort
se envía de entrada a more. El resultado final consiste en mostrar las líneas del
fichero /etc/passwd, ordenadas y haciendo una pausa cada vez que se llena la pantalla.
La forma en que enlazamos las diferentes órdenes no es aleatoria, sino que se
debe hacer de manera adecuada para obtener los resultados esperados. 
En la construcción de pipelines largas conviene ser metódicos para evitar
errores, es preferible hacer la pipeline paso a paso e ir comprobando los resultados
intermedios para corregir los posibles errores. El proceso a seguir sería el siguiente:
1.- Comprobamos las dos primeras órdenes de la pipeline: orden1 | orden2
2.- Si el resultado es correcto, entonces añadimos la orden siguiente del
proceso y comprobamos el resultado intermedio: orden1 | orden2 | orden3
3.- Si el resultado es correcto, añadimos la siguiente: orden1 | orden2 | orden3
| orden 4
4.- Y así sucesivamente hasta obtener el pipeline final.

ANTONIO J. LEÓN DELGADO


26
UD 01: Scripting con bash
No todas las órdenes se pueden enlazar de cualquier manera con tuberías. Para
hacer una pipeline debemos tener en cuenta las siguientes consideraciones:
1.- Una orden puede tener a la derecha una tubería si admite salida
estándar. Por ejemplo, las órdenes ls, who, date, cat, etc.
2.- Una orden puede tener a la izquierda una tubería si admite entrada
estándar. Por ejemplo, las órdenes wall, write, etc.
3.- Una orden puede tener una tubería a su izquierda ya su derecha si admite
tanto entrada como salida estándares. Por ejemplo, las órdenes sort , cut , grep , etc.

Observación: one liners


Entre los administradores de sistemas es común el término one liners. Se
refiere a un conjunto de órdenes unidas, generalmente, por tuberías que enlazadas
adecuadamente producen un resultado útil y práctico. Por ejemplo, si queremos saber
cuáles son los cinco procesos que consumen más CPU podemos ejecutar el siguiente:
ps -eo pcpu, user, pid, cmd | sort -r | head -6  esto es un comando
complejo  crear script como práctica (ps_pesados.sh en /usr/local/bin)
Hemos utilizado la opción o de la orden ps, que permite personalizar la salida, y
hemos indicado las columnas que queremos que se muestren. La opción e dela orden
indica extended, es decir, los procesos de todos los usuarios. Toda la salida de ps es
enviada a sort, que hace un ordenamiento inverso (opción r) basándose en la primera
columna (pcpu). Por último, la salida ordenada es enviada a la orden head, que
mostrará las seis primeras líneas (la cabecera y los cinco procesos con más consumo).

Filtros y 'pipelines'
En Unix hay unas órdenes que se utilizan mucho en las pipelines y que
llamamos filtros. 
Los filtros son órdenes que aceptan datos de la entrada estándar, escriben la
salida a la salida estándar y escriben los errores en la salida estándar de errores, y
nunca modifican los datos de entrada. Debido a su funcionamiento, es frecuente
utilizar filtros en las pipelines. Por ejemplo, un filtro muy utilizado es sort, que sirve

ANTONIO J. LEÓN DELGADO


27
UD 01: Scripting con bash
para ordenar líneas de texto. La orden acepta datos desde la entrada estándar (el
teclado). Para introducirlas sólo hay que escribir la orden y pulsar la tecla de retorno:
$sort [Intro]
El cursor se sitúa debajo esperando líneas de entrada. Escribimos las líneas de
texto que queremos que la orden ordene separadas por saltos de línea y, al terminar,
debemos pulsar la combinación de teclas Ctrl + D para indicar la finalización de la
entrada de datos. A continuación, nos aparecerán por pantalla las líneas ordenadas.
Para comprender las pipelines del ejemplo es recomendable que las ejecute
comprobando los resultados intermedios, siguiendo el proceso descrito en el apartado
"Tuberías o pipes".
Como los filtros aceptan los datos del teclado (entrada estándar) y muestran el
resultado por la pantalla (salida estándar), podemos utilizarlos en pipelines para
procesar mediante una tubería la salida de cualquier orden que dé el resultado en
líneas de texto. Por ejemplo:
$who | sort
La orden who muestra una línea para cada usuario conectado al sistema. Al
enviar esta salida a la entrada de sort, las líneas se muestran ordenadas por pantalla.

Veamos otro ejemplo:


$cat /etc/group | sort
La orden cat muestra las líneas del fichero /etc /group. Al enviar esta salida a la
orden sort, las líneas del archivo se muestran ordenadas por pantalla, pero el archivo
no se modifica.
Los filtros no sólo aceptan datos desde la entrada estándar, sino que también
aceptan datos desde uno o más ficheros puestos como argumentos en la línea de
comandos. Entonces, la orden anterior también se puede escribir así:
$sort /etc/group
En general los filtros son muy útiles para procesar el contenido de los archivos
de texto. Algunos de los más utilizados son wc, sort, grep y cut:
Contar el número de líneas de un archivo:
$wc -l /etc/passwd
Ordenar alfabéticamente las líneas de un archivo:
$sort /etc/passwd
Hacer la búsqueda de líneas que contengan un patrón determinado:
$grep root /etc/passwd
Extraer partes de las líneas del archivo:
$cut -d: -f1 /etc/passwd
Ejemplos de construcción de pipelines con filtros
Cuenta los archivos del directorio actual.
$ls | wc -l
Muestra los nombres (sólo los nombres) de los usuarios dados de alta en el
sistema ordenados alfabéticamente.
$cat /etc/passwd | cut -f1 -d”:” | sort
Muestra los nombres de los usuarios dados de alta en el sistema y su shell de
inicio, ordenados alfabéticamente y separando los dos campos por un tabulador.
$cat /etc/passwd | cut -f1, 7 -d”:” | sort | tr ":" " \t "

ANTONIO J. LEÓN DELGADO


28
UD 01: Scripting con bash
Muestra el identificador (PID) y el nombre (CMD) de todos los procesos que
pertenecen al usuario root.
$ps -ef | grep "^root" | tr -s "" | cut -f2, 8 -d ""

Observación: tr -s “ “ hace que el carácter expresado entre “ “ solo aparezca


una vez en caso de que se repita.
Otro ejemplo: muestra una lista con el propietario, grupo y nombre de archivo
de todos los archivos que hay en /etc. La lista debe estar ordenada por el nombre del
grupo del archivo.
$ls -l /etc | tr -s " " | cut -f3, 4 ,9 -d" " | sort -k2 -t " "

2. - Programación del shell Bash


El shell Bash (Bourne-Again Shell) es un intérprete de comandos, un programa
informático que tiene la función de interpretar órdenes. El shell nos proporciona la
posibilidad de programar la ejecución de un conjunto de comandos con su propio
lenguaje y de almacenarlas en un fichero, que ejecutaremos como cualquier otra
orden del sistema. Este archivo de órdenes se denomina guion de shell o shell
script. Se pueden escribir guiones de shell complejos, ya que el Shell admite variables,
parámetros, entrada y salida de datos interactiva, comparaciones, bifurcaciones,
bucles, etc.
La única manera de aprender shell scripting es haciendo guiones de shell. Por
eso es muy recomendable que a medida que vayas avanzando en la lectura de este
apartado copies los ejemplos y los pruebes en tu sistema. También debes ir haciendo
las actividades que se irán proponiendo.

Creación y ejecución de un guion de shell


Un guion de shell o shell script es un conjunto de comandos almacenados en un
archivo de texto plano. Podrán ser ejecutadas posteriormente con el nombre del
archivo que los contiene. En el guion podemos incluir la llamada a cualquier programa
que sea ejecutable por shell (órdenes del sistema, otros guiones de shell, etc.), así
como llamadas a funciones, estructuras de control del lenguaje del shell, etc.
Cada orden que escribimos en un guion debe ir separada por un salto de línea o
bien por el carácter ; (punto y coma) si está en la misma línea. Las instrucciones del
guion se ejecutan seguidas una tras otra en el orden en que están escritas, como si las
estuviéramos escribiendo una a una en la línea de comandos, y el salto de línea o el
ANTONIO J. LEÓN DELGADO
29
UD 01: Scripting con bash
carácter punto y coma se interpretan como si pulsamos Intro o Return después de
cada orden.

Creación y nombre del archivo


Para crear un shell script basta con abrir un nuevo archivo vacío en un editor de
texto, escribir la secuencia de comandos que queremos que se ejecuten y guardar el
archivo con el nombre que le queremos dar al guion. El archivo del guion de shell debe
estar compuesto únicamente por texto sin formato y por ello tenemos que utilizar
un editor de texto plano.
Editores de texto avanzados
Los editores de texto vi (o el vim), nano y emacs se han utilizado históricamente
en entornos Unix, pero pueden resultar algo incómodos. Existen otros más fáciles de
utilizar, como gedit que se encuentra por defecto en el entorno de escritorio GNOME,
Sublime Text, Atom, etc. que, seguramente, deberemos instalar. Los editores de
textos planos se distinguen de los procesadores de textos en que se usan para escribir
sólo texto, sin formato y sin imágenes. También podemos optar por usar un editor de
texto avanzado que reconozca el lenguaje del shell y utilice diferentes colores para el
resaltado de la sintaxis.  Este tipo de editores son especialmente útiles cuando somos
principiantes, ya que nos ayudan a prevenir errores de sintaxis, como olvidar de poner
una llave de cierre, unas comillas dobles, etc.
Práctica: instala algún editor adecuado a la creación de scripts.
Instalo Sublime Text y, entre otras cosas, me permite ejecutar desde dentro los
scripts (Tools / Build)

ANTONIO J. LEÓN DELGADO


30
UD 01: Scripting con bash
El nombre que damos al archivo puede ser cualquier nombre válido de acuerdo
con las normas que hay para nombrar archivos en el sistema en el que estamos
trabajando. Normalmente elegiremos un nombre que sea representativo de lo que
hace el guion, por ejemplo, si hace una copia de seguridad, podemos llamarlo copia. En
todo caso conviene que nos aseguramos de que el nombre no entra en conflicto con
otros programas o comandos existentes en el sistema. Para garantizar esto, los
nombres de los guiones a menudo terminan con la extensión .sh.
Podemos nombrar nuestros guiones con la extensión .sh. Ahora bien, esto sólo es una
convención y no es obligatorio hacerlo.
Recuerda: un script programado con cron y ubicado en /etc/cron.hourly (daily,
weekly, …), no debe tener extensión.

Nombres de ficheros
Recuerda: las órdenes which, whereis y locate nos sirven para verificar si ya
existen archivos con un nombre determinado. Otras órdenes de búsqueda: apropos y
whatis
Para empezar, abre un editor de texto y crea un nuevo archivo que contenga
las cuatro líneas siguientes:
#!/bin/bash
# Ejemplo de shell básico
echo "Hola, mundo"
echo "Soy $USER "
Aunque ahora no queremos analizar a fondo el significado de cada una de las líneas
anteriores, hacemos una descripción breve para saber qué hace el guion:
1.- La primera línea indica al sistema que el programa Bash ubicado en el
directorio /bin debe ejecutar el resto de instrucciones que hay en el script.
2.- La línea siguiente es un comentario y, por tanto, el shell la ignora.
3.- La tercera línea es una orden echo, que permite mostrar texto por pantalla,
en este caso, la frase "Hola, mundo".
4.- La última línea es otra orden echo que muestra por pantalla el mensaje “Soy
$USER” sustituyendo el valor de la variable USER por el nombre del usuario que
ejecuta el guion.
Una vez hemos escrito las líneas sin errores, guardamos el archivo y le damos
un nombre para poder ejecutarlo desde la línea de comandos.

Ejecución del guion de shell


La manera más habitual de ejecutar un script es abrir una sesión de terminal y
ejecutarlo como cualquier otra orden del sistema, es decir, escribiendo el nombre del
archivo que contiene el script en la línea de comandos seguido de un salto de
línea. Para poder hacerlo es necesario que el archivo tenga permiso de ejecución para
ANTONIO J. LEÓN DELGADO
31
UD 01: Scripting con bash
los usuarios que tienen que ejecutarlo. En el caso más simple, daremos permiso de
ejecución al propietario del fichero con la siguiente orden:
chmod u+x nombrearchivo
Después de añadir el permiso de ejecución al fichero, podemos ejecutarlo con
su nombre teniendo en cuenta las siguientes consideraciones:
1.- Si el directorio que contiene el fichero del shell script está en la variable
PATH, podemos poner sólo el nombre del archivo que contiene el guion para que se
ejecute:
# Ejecutar script
nombre_script
2.- Si el directorio que contiene el fichero del shell script no está en el PATH,
tenemos que escribir el nombre del archivo indicando donde se encuentra (con ruta
relativa o camino absoluto): 
# Ejecutar un guion de shell des del directorio actual
./nombre_script (ruta relativa)
# Ejecutar un guion de shell ubicado /home/usuario
/home/usuario/nombre_script (ruta absoluta)

Variable PATH
La variable PATH contiene la lista de directorios donde el shell se dirigirá para
localizar las órdenes que ejecutamos. Esto nos permite escribir las órdenes
escribiendo sólo su nombre y sin especificar el directorio donde están situadas, es
decir, sin preocuparnos de dónde se encuentran en el disco. La búsqueda de la orden
solicitada en los directorios que se especifican en la variable PATH se hace de
izquierda a derecha. Si no encuentra el orden en ninguno de los directorios
especificados en la variable PATH, el shell avisa con un mensaje de error que no se ha
encontrado la orden. Puedes ver el valor de la variable PATH con la orden: 
$echo $PATH.

Ejemplo de creación y ejecución de un guion de shell


Abra una sesión de terminal, asegúrate de que estás situado en tu directorio de
inicio (~ - lo obtenemos pulsando en la tecla F5, AltGr + 4 -) ejecutando la siguiente
orden: cd
Con un editor de texto plano, crea un archivo que contenga las siguientes
líneas:
#!/bin/bash
# holamundo.sh
echo “Hola, mundo"
echo “Hoy es $(date +% x) "
echo “Soy $USER "# también ${USER}
echo “Trabajo con el sistema $(uname -sr) "# también `uname -sr`
echo “Adiós"

ANTONIO J. LEÓN DELGADO


32
UD 01: Scripting con bash

Guarda el archivo en el mismo directorio de inicio y llámalo holamundo.sh. Dale


permiso de ejecución al archivo:
chmod u+x holamundo.sh

Ejecuta el shell script indicando el nombre de donde se encuentra con ruta


relativa:
./holamundo.sh
Ejecuta de nuevo el shell script. Ahora indicando el fichero con ruta absoluta:
~/holamundo.sh ó /home/usuario/holamundo.sh
Crea un directorio ~/scripts para guardar tus programas, mueve el archivo
holamundo.sh en el directorio ~ / scripts y añade el directorio al contenido de la
variable PATH con los siguientes comandos:
mkdir ~/scripts
mv ~/holamundo.sh ~/scripts
export PATH = " $PATH:~/scripts"
Ahora puedes ejecutar holamundo.sh llamándolo sólo por su nombre, sin
indicar donde se encuentra (ni con camino relativo ni con camino absoluto), porque
está en un directorio contenido en la variable PATH.
Puedes intentar situar a cualquier punto de la jerarquía de directorios del
sistema y ejecutar holamundo.sh poniendo sólo su nombre. Por ejemplo:
cd /tmp
holamundo.sh
El cambio que has realizado a la variable PATH sólo afecta a la sesión de shell
que tienes activa. Si quieres que la variable PATH tenga el directorio ~/scripts en todas
las sesiones que abras, hay que editar el archivo ~/.bashrc y añadir al final la línea
siguiente:
PATH = "$PATH:~/scripts"
Un guion de shell también se puede ejecutar explícitamente con un Shell
determinado, poniendo el nombre del shell ya continuación el nombre del archivo que
contiene el guion de shell:
bash holamundo.sh
Este método generalmente sólo lo usamos cuando queremos comprobar que el
programa funciona con otro shell o cuando queremos depurar (debug) el guion
de shell. 
Por ejemplo:
rbash nombre_script # Ejecutar el guion con rbash
sh nombre_script # Ejecutar el guion con sh
bash -x nombre_script # Ejecutar el guion con modo debug (opción x)

Cuando el bash ejecuta un shell script, crea un proceso hijo que ejecuta otro
bash, el cual lee las líneas del archivo, las interpreta y ejecuta como si vinieran del
ANTONIO J. LEÓN DELGADO
33
UD 01: Scripting con bash
teclado. El proceso bash padre espera mientras el bash hijo ejecuta el script hasta el
final, y en ese momento el control vuelve al proceso padre, el cual vuelve a poner el
indicador o prompt. Los cambios en el entorno de un shell hijo no afectan al entorno
del shell padre.
Por lo tanto, si en el guion hay órdenes que modifican el entorno, tales como
cambiar de directorio actual, modificar el valor de una variable de entorno, crear una
nueva variable, etc., estos cambios sólo tienen efecto en el entorno del shell hijo y
desaparecen una vez finaliza la ejecución del guion.
Si queremos ejecutar un guion en el shell actual en lugar de con un shell hijo y,
por tanto, modificar el entorno actual, debemos ejecutar el guion con la orden source.
source nombre_script # Ejecutar en el shell actual

Observación: la orden del Bash llamada source es un sinónimo de la


orden . (punto) del shell Bourne: . nombre_script
No confundir la orden punto (.) del shell Bourne con el directorio punto, que
indica el directorio actual.

Ejemplo de ejecución de un guion de shell con o sin modificación del entorno


actual
Crea un guion de shell en su directorio de trabajo que contenga las siguientes
líneas:
#!/bin/bash
cd /etc
echo El directorio actual es:
pwd
Guarda el archivo y llámalo cambio.sh. Dale permiso de ejecución al archivo:
chmod u + x cambio.sh
Ejecuta el guion desde el directorio actual mediante un shell hijo:
$./cambio.sh
La salida le muestra que en el shell hijo se cambia de directorio. Ahora bien,
cuando finaliza la ejecución del guion, el directorio donde estamos situados sigue
siendo nuestro directorio de trabajo, para que el cambio en el shell hijo no nos
afecta. Lo comprobamos ejecutando:
$pwd
Ejecuta el guion en el shell actual (en lugar de en un shell hijo) mediante la
orden source o la orden punto (.):
$source cambio.sh
$. cambio.sh
En este caso, al finalizar la ejecución nos encontramos en el directorio /etc, ya
que las órdenes del guion se han ejecutado en nuestro shell. Lo comprobamos
ejecutando:
$pwd

Script

ANTONIO J. LEÓN DELGADO


34
UD 01: Scripting con bash

Resultado secuencia de ejecución

Definición del shell de ejecución


Al escribir guiones de shell es recomendable que indicamos el shell que debe
ejecutar el guion. Por eso, los dos primeros caracteres de la primera línea deben
ser #!, seguidos del nombre del shell que debe interpretar las órdenes que vienen a
continuación.
Por ejemplo:
#!/bin/bash
Observación: no empieces el shell script con una línea en blanco, para que
estas líneas también sean tenidas en consideración.
Si omitimos la definición del shell de ejecución, el shell script se ejecuta con
el shell lo establecido por defecto y, en el caso de no ser el shell para el que se ha
escrito el guion, las órdenes contenidas pueden dar error al ser ejecutadas.

Comentarios al guion de shell


Los comentarios son útiles para ayudar a entender los scripts. Hay que tener en
cuenta que probablemente no seremos los únicos que vamos a leer el código de los
guiones de shell que hacemos, o que, pasado un tiempo, la memoria puede fallar y no
entender el programa que nosotros mismos hemos escrito.
Normalmente, las primeras líneas después de la línea que indica el shell de
ejecución son un comentario sobre la funcionalidad del programa. El resto del
programa se comenta como sea necesario para una mayor claridad y comprensión del
código.
Observación: ver scripts de inicio de sistema como ejemplos de scripts
comentados
Ver los scripts de inicio del sistema en el directorio /etc/init.d. Estos guiones
están muy bien comentados para que sean fácilmente legibles por los administradores
y los programadores del sistema.

ANTONIO J. LEÓN DELGADO


35
UD 01: Scripting con bash
Para añadir comentarios al programa sólo hay que poner el símbolo # seguido
del texto que se quiera. Cada nueva línea debe ir precedida del símbolo #. Se puede
poner el símbolo \ al final de una línea para indicar que el texto no ha terminado y
continúa en la línea siguiente (válido para comandos y comentarios). En /etc/init.d hay
buenos ejemplos de comentarios en scripts. Edita, por decir alguno, kerneloops

Tabulación del código


En el caso más simple, un guion no es más que una lista de comandos del
sistema escritas una detrás de la otra que se ejecutan de manera secuencial. Ahora
bien, al programar shell scripts, a menudo utilizamos estructuras de control de flujo
(condicionales e iterativas) que nos permiten romper el flujo de ejecución, repetir una
parte de las órdenes, etc., y que hacen que el guion de shell no sea simplemente una
enumeración de comandos.
Para facilitar la lectura del código del guion de shell, es muy importante que el
código esté bien tabulado. Bash no necesita que haya tabuladores para poder
interpretar el código, pero a las personas sí que nos va bien que el código esté
organizado con tabuladores para leerlo de una manera cómoda.

Recomendación: el código de un shell script debe estar bien tabulado para que


el programa sea legible.
Por ejemplo, consideremos el guion de shell siguiente, escrito sin tabular:
#! /bin/bash
echo -n "Escribe un número:"
read X
if [ $X -lt 10 ] ; then
echo "$X es menor que 10"
else
if [ $X -gt 10 ] ; then
echo “$X es mayor que 10"
else
echo “$X es igual a 10"
fi
fi
La lectura y la comprensión de las estructuras condicionales anidadas del guion
de shell anterior son difíciles. El mismo código, tabulado y comentado, queda así:
ANTONIO J. LEÓN DELGADO
36
UD 01: Scripting con bash
#! /bin/bash
set -u
# Pide un número y comprueba si es igual a 10.
echo -n "Escribe un número:"
read X
if [ $X -lt 10 ] ; then
echo “$X es menor que 10"

else
if [ $X -gt 10 ] ; then
echo “$X es mayor que 10"
else
echo “$X es igual a 10"
fi
fi

La tabulación nos permite leer el código y seguir la lógica del programa con más
comodidad, así como evitar errores de programación (olvidar una palabra clave de
cierre de una estructura de control, situar una palabra clave en algún lugar indebido,
etc.).
No hay ninguna norma que fije cómo se debe tabular el código; hay quien
utiliza tabuladores de dos espacios y quien prefiere cuatro espacios. Lo que importa es
utilizar un estilo homogéneo a lo largo de todo el shell script.
Si usamos un editor de texto plano, debemos sangrar el código a mano, o bien
con espacios o bien con tabuladores. Algunos editores avanzados nos facilitan este
trabajo haciendo que a medida que escribimos el código se vaya “sangrando” de
manera automática sin que nosotros nos tengamos que preocupar de las tabulaciones.

Depurar un guion de Shell


bash -x s.sh  traza
Dentro del código:
set -x, set +x
A veces el funcionamiento de un guion de shell no es el esperado y tenemos
que determinar cuál es la causa de este funcionamiento incorrecto, es decir, tenemos
que depurar el shell script. Una técnica consiste en hacer un seguimiento paso a paso
de las órdenes que ejecuta para ver donde se produce el error. Para realizar este
seguimiento podemos ejecutar el script con bash -x seguido del nombre del guion así:
bash -x nombrearchivo
O bien podemos incluir -x en la primera línea:
#!/bin/bash -x
Si sólo queremos depurar una parte del programa, podemos hacerlo de la
siguiente manera:
...
# Activar depuración desde aquí
set -x
  código para depurar
ANTONIO J. LEÓN DELGADO
37
UD 01: Scripting con bash
  # Parar la depuración
set +x

La opción -x de Bash indica que se han de imprimir por pantalla las órdenes y
sus argumentos mientras se ejecutan. Así podemos ver qué punto de la ejecución se ha
llegado cuando se produce un error.

Ejemplo de ejecución paso a paso de un guion de shell


Hacer un guion de shell que se llame prueba.sh con las líneas siguientes:
#! /bin/bash
N=5
echo "El valor de N es: $N "
Ejecuta de manera normal y observa la salida es simplemente el mensaje
El valor de N es: 5
Ahora lo ejecutamos mostrando cada paso de la ejecución de la siguiente
manera:
bash -x prueba.sh
El resultado es que nos aparecen las líneas que se van ejecutando con un
símbolo + delante:
+ N=5
+ echo ‘El valor de N es: 5'
El valor de N es: 5

Podemos añadir la opción -v para mostrar todas las líneas de entrada al shell,
incluyendo comentarios, a medida que las va leyendo:
bash -xv prueba.sh
El resultado es:
#!/bin/bash
# Shell script de prueba
N=5
+ N=5
echo “El valor de N es: $N”
+ echo ‘El valor de N es: 5'
El valor de N es: 5

Otros ejemplos
1.- Depurando todo el programa

ANTONIO J. LEÓN DELGADO


38
UD 01: Scripting con bash

2.- Depurando en partes del programa


En este ejemplo, edito de nuevo uno.sh y establezco donde empieza (set -x) y
donde termina (set +x) la depuración

Ejecución:

ANTONIO J. LEÓN DELGADO


39
UD 01: Scripting con bash

Interacción con el usuario


Un guion de shell puede requerir interacción con el usuario, es decir, solicitarle
la entrada de datos o mostrarle datos de salida. Para poder interaccionar con el
programa normalmente utilizamos las órdenes echo y read, que nos permiten mostrar
datos en la pantalla del terminal y leer datos del teclado en modo texto.
Si lo que queremos es hacer una aplicación gráfica, entonces bash no es la
herramienta indicada. Sin embargo, podemos tener una interacción básica con modo
gráfico y utilizar cajas de diálogo sencillas con algún software que lo permita, como el
Zenity.
Órdenes echo y read
1.- La orden echo muestra una cadena de texto añadiendo un salto de línea
para la salida estándar. La sintaxis de la orden es:
echo [ opción ] [ cadena ]
Ejecuta man echo para consultar la lista de caracteres de escape posibles de su
sistema.
Las opciones que se pueden utilizar con echo son:
-n para que no añada un salto de línea después de mostrar la cadena.
-e para habilitar la interpretación de los caracteres de escape ( \n para añadir
un salto de línea, \ t para añadir un tabulador, etc.). Si utilizamos esta opción hay que
ponemos el texto de la cadena entre comillas dobles.
Por ejemplo:
$echo -e "\n\nHola\t, mundo \n\n "

2.- La orden read permite leer datos de la entrada estándar. La utilización más


habitual del orden read es con la siguiente sintaxis:
read nombre_variable
La orden read lee lo que el usuario introduce por el teclado hasta que hay un
salto de línea y asigna los datos a la variable nombre_variable. Por ejemplo:
read N
Ejecuta man read para ver la sintaxis completa de esta orden.

Ejemplo de guion de shell que interacciona con el usuario con las órdenes echo
y read.

ANTONIO J. LEÓN DELGADO


40
UD 01: Scripting con bash
Hacer un guion de shell que se llame saludo.sh que pida un nombre ya
continuación muestre un mensaje de saludo con el nombre que hemos introducido.
#!/bin/bash
# saludo.sh
# Ejemplo de uso de las órdenes echo y read
echo "¿Cómo te llamas?"
read NOMBRE
echo "Hola, $NOMBRE "
Observación:
read -p "¿Cómo te llamas?" NOMBRE equivale a
echo "¿Cómo te llamas?"
read NOMBRE
Interacción en modo gráfico
El shell Bash es un shell de línea de comandos pensado para interactuar en
modo texto, pero podemos tener una interacción básica en modo gráfico utilizando
otros programas, tales como Zenity. Zenity permite utilizar cajas de diálogo basadas en
GTK+ en la línea de comandos y en los shell scripts.

Observación: herramientas GTK


Las GTK o grupo de herramientas del GIMP (GIMP toolkit, en inglés) son unas
librerías pensadas para el desarrollo de aplicaciones gráficas con facilidad. Disponen de
licencia GPL y hay numerosos proyectos que las utilizan, como el GIMP (de ahí le viene
el nombre, ya que inicialmente se crearon para este proyecto), GNOME, el Eclipse o
Firefox, entre otros.
Podemos usar Zenity para crear diálogos simples que interactúen gráficamente
con el usuario, ya sea para obtener información del usuario o para proporcionarle
información. Por ejemplo, podemos pedir al usuario que seleccione una fecha de un
calendario (diálogo de calendario) o un archivo (diálogo de selección de un archivo), o
se puede usar un diálogo de progreso para indicar el estado actual de una operación o
usar un diálogo de alerta para notificar al usuario algún error.
Por ejemplo, abre una sesión de terminal desde el escritorio (si es Gnome o
XFCE funciona seguro; en otros habrá que probarlo) y ejecuta la línea de comandos
siguiente:

El resultado es un cuadro de diálogo como el que se muestra en la siguiente


imagen.

ANTONIO J. LEÓN DELGADO


41
UD 01: Scripting con bash

Parámetros y variables especiales


La utilización de parámetros es un método para pasar datos al programa de
manera no interactiva. El lenguaje del shell dispone de una serie de variables
especiales que permiten trabajar con estos parámetros.
Uso de parámetros
Un parámetro o argumento de la línea de comandos no es más que un valor
que le pasamos al programa en el momento de su llamada. Un programa puede tener
cualquier número de argumentos en la línea de comandos.
La mayoría de las órdenes de Unix pueden hacer acciones diferentes en función
de los parámetros que les damos en ejecutarlas. Por ejemplo, en la siguiente orden:
ls /etc
ls es el nombre de la orden que se debe ejecutar. Todo lo que viene a
continuación en la línea de comandos se toma como argumentos para esta
orden. Cada uno de los argumentos está separado por uno o más espacios. En este
caso hay un único argumento, el nombre del directorio /etc.
Consideremos un ejemplo de llamada de una orden con dos argumentos:
tail +10 /var/log/messages
tail es el nombre de la orden,  +10 es el primer argumento y el nombre del
archivo /var/log/messages es el segundo argumento.

Del mismo modo que lo hacen la mayoría de las órdenes, los guiones de shell
pueden aceptar parámetros cuando se ejecutan. Un parámetro o argumento es un
valor que le damos al guion de shell en el momento de su llamada. Los argumentos de
un shell script se pueden referenciar dentro del programa mediante una serie
de variables especiales.

ANTONIO J. LEÓN DELGADO


42
UD 01: Scripting con bash

Variables especiales
Al ejecutar un guion de shell con parámetros, hay un conjunto de variables
especiales del shell llamadas parámetros posicionales que se fijan automáticamente
para que coincidan con los parámetros dados en el programa. Se llaman parámetros
posicionales porque la asignación de cada variable depende de la posición de un
parámetro en la línea de comandos. Los nombres de estas variables se corresponden
con el valor numérico de su situación en la línea de comandos: 0, 1, 2, 3 ... Y así hasta
el último parámetro que se pasa.
Los parámetros posicionales se pueden utilizar dentro del guion de shell como
cualquier otra variable del shell, es decir, para saber su valor utilizaremos el
símbolo $. A partir del décimo parámetro, el número debe cerrarse entre llaves.
Los parámetros dentro del programa son accesibles utilizando las
variables: $0, $1, $2, $3... ${10}, ${11}, ${12}...
Ejemplo de uso de parámetros en un shell script
Crea un shell script que se llame args.sh con el siguiente contenido:
#! /bin/bash
# args.sh
# Ejemplo de uso de parámetros
echo “Nombre del guion de shell: $0"
echo “Valor del primer parámetro del guion de shell: $1"
echo “Valor del segundo parámetro del guion de shell: $2"
echo “Valor del tercer parámetro del guion de shell: $3"
Observación: podemos poner `basename $0` para evitar mostrar la ruta
entera.

Guarda el archivo y dale permiso:


$chmod u+x args.sh
Ejecútalo así:

ANTONIO J. LEÓN DELGADO


43
UD 01: Scripting con bash
$./args.sh azul verde rojo
La salida del programa es la siguiente:
Nombre del guion de shell: ./args.sh
Valor del primer parámetro del guion de shell: azul
Valor del segundo parámetro del guion de shell: verde
Valor del tercer parámetro del guion de shell: rojo

Puedes probar diferentes ejecuciones del programa que acabas de hacer y


comprobar los resultados. Por ejemplo:
. /args.sh A B C
. /args.sh Juan Antonio Luis

Además de los parámetros posicionales, hay otras variables especiales definidas


en cualquier script que podemos usar según nuestras necesidades. La siguiente tabla
muestra el nombre y la descripción de las variables especiales más utilizadas.

Tener en cuenta las siguientes observaciones:


1.- $* Y $ @ son lo mismo cuando no van entre comillas. El espacio es el
separador de parámetros y obvia las comillas si es que aparecen.
2.- “$*” expande los parámetros en una cadena: "par1 par2 ...". Es una sola
cadena que comprende todos los parámetros unidos por espacios en blanco. Por
ejemplo ‘1 2’ 3 se convierte en "1 2 3".
3.- “$@” expande los parámetros en cadenas diferenciadas: "par1" "par2" ... Es
decir, la lista de cadenas resultante coincide exactamente con lo que se ha dado en el
guion de shell. Por ejemplo, '1 2' 3 se convierte en "1 2" "3".
Mejor con un ejemplo:

Ejecutamos y no tiene en cuenta las comillas

ANTONIO J. LEÓN DELGADO


44
UD 01: Scripting con bash
Ahora, en lugar de $* ponemos $@. Obtenemos el mismo resultado

Ahora vamos a poner “$*” y “$@. Aquí si hay diferencias:

Con “$*” expande los parámetros en una cadena.

Con “$@” expande los parámetros en cadenas diferenciadas.

El valor de las variables especiales se puede guardar en otras variables, por


ejemplo:
NOMBRE=$1
Ahora bien, la asignación de valores a las variables especiales no está permitida,
por ejemplo:
# Operación no permitida
$1=”Tony”

ANTONIO J. LEÓN DELGADO


45
UD 01: Scripting con bash

Ejemplo de visualización de los parámetros y de otras variables especiales.


Modifica el guion de shell args.sh y añade al final las líneas que aparecen en
negrita:
#!/bin/bash
# args.sh
# Ejemplo de uso de parámetros
echo "Nombre del guion de shell: $0"
echo “Valor del primer parámetro del guion de shell: $1"
echo “Valor del segundo parámetro del guion de shell: $2"
echo “Valor del tercer parámetro del guion de shell: $3"
echo “Número de parámetros pasados al guion de shell: $#"
echo “Lista de todos los argumentos recibidos: $*"

Ejecútalo:
. /args.sh a b c
La salida del programa es la siguiente:
Nombre del guion de shell: ./args.sh
Valor del primer parámetro del guion de shell: a
Valor del segundo parámetro del guion de shell: b
Valor del tercer parámetro del guion de shell: c
Número de parámetros pasados al guion de shell: 3
Lista de todos los argumentos recibidos: a b c

¿Es lo mismo ./args.sh abc?

Control del número de parámetros


La mayoría de las órdenes muestran un mensaje de error o aviso cuando los
argumentos requeridos por la orden no se han especificado en su llamada. Por
ejemplo, ejecuta el comando:
rm
La salida que le da es:
rm: missing operando
Try `rm --help 'for more information.
Análogamente, si un guion de shell espera recibir parámetros, debemos
verificar dentro del programa que la llamada al guion de shell se ha hecho con el
número de parámetros esperado y, en caso contrario, mostrar un mensaje de
error. Este control lo podemos llevar a cabo mediante la estructura condicional if (ya lo
veremos más adelante) y la variable especial $#.
ANTONIO J. LEÓN DELGADO
46
UD 01: Scripting con bash

Ejemplo de un shell script con control de número de parámetros


Crea un shell script con el siguiente código y llámalo suma.sh. Este script recibe
dos números por parámetro y muestra por pantalla el resultado de la suma de los dos
números. Al inicio controla que el número de argumentos recibidos es correcto. En
caso contrario, muestra un aviso.
#! /bin/bash
# suma.sh
# Recibe dos números por parámetro y muestra la suma.
# Control del número de parámetros:
if [ $# -ne 2 ] ; then
echo "Error: se esperaban dos parámetros."
echo "Uso del programa: $0 num1 num2"
exit 1
fin
# Recibimos dos parámetros, hacemos la suma
((SUMA = $1 + $2))
echo "La suma de $1 y $2 es: $SUMA "
Dale permiso de ejecución al archivo y ejecútalo pasándole dos números como
parámetros:
chmod +x suma.sh
. /suma.sh 10 5
La salida del programa es la siguiente: 15
Ahora, no le pases dos parámetros. Por ejemplo:
./suma.sh
La salida del programa es la siguiente:
Error: se esperaban dos parámetros.
Uso del programa: suma.sh num1 num2

Códigos de salida
Al finalizar la ejecución todas las órdenes generan un código de salida (en
inglés, exit status o exit code) que es un número entero entre 0 y 255.
Por convención, si la orden acaba bien, normalmente devuelve un cero (0) y si
acaba mal devuelve un valor distinto de cero (entre 1 y 255). Muchas veces, el valor
devuelto por la orden representa el error generado. Por ejemplo, los errores de
sintaxis casi siempre hacen que las órdenes devuelvan el valor 1.
El shell nos proporciona una variable especial llamada “?” (signo de
interrogación sin las comillas), que contiene el código de salida de la orden ejecutada
anteriormente.

ANTONIO J. LEÓN DELGADO


47
UD 01: Scripting con bash
El código de salida puede ser utilizado por el Bash, lo podemos mostrar o
podemos controlar el flujo del guion con él. Para visualizar el valor utilizamos la
orden echo:
$echo $?
En los guiones de shell, la mayoría de las decisiones de programación se
controlan analizando el valor de los códigos de salida. Cuando evaluamos condiciones,
el código de salida nos indica si la condición es verdadera (devuelve 0) o falsa
(devuelve un valor distinto de cero).
Ejemplo de ejecución de órdenes y consulta de los códigos de salida
Abre una sesión de terminal y ejecuta el comando ls:
ls
Visualiza el código de salida de la orden que acaba de ejecutar:
echo $?
0
La salida es un cero, lo que indica que no ha habido ningún error. Ejecuta el ls
con algún nombre de archivo inexistente para provocar un error:
ls asdfg
ls: no se ha podido acceder a asdfg: El archivo o directorio no existe
Visualiza el código de salida de la orden que acaba de ejecutar:
echo $?
2
La salida es un valor distinto de cero (un 2), lo que indica que ha habido un
error.
Ejecuta el cp sin argumentos para provocar un error:
cp
cp : falta un operando archivo
Pruebe « cp --help» para obtener más información.
Visualiza el código de salida de la orden que acaba de ejecutar:
echo $?
1
La salida es un 1, lo que indica error de sintaxis.

Del mismo modo que lo hacen las órdenes, los shell scripts devuelven un código
de salida que es el de la última orden ejecutada. El código de salida de un shell
script también se puede consultar justo después de la finalización del programa
mediante la variable $?

Ejemplo de visualización del código de salida de un guion de shell


Crea un guion de shell sencillo llamado hola.sh:
#!/bin/bash
echo "Hola, mundo"

Dad permiso de ejecución al fichero y ejecute el guion:


$chmod u+x hola.sh
$./hola.sh
Consulta el código de salida desde la línea de comandos:
echo $?  0
ANTONIO J. LEÓN DELGADO
48
UD 01: Scripting con bash

El código devuelto es un cero, el de la última orden ejecutada en el guion


de shell, es decir, la orden echo “Hola, mundo”.

Puedes modificar el guion y añadir al final una orden que dé error para
comprobar el efecto que tiene sobre el código de salida.

Orden exit
La orden exit provoca la finalización del shell script y de manera opcional
permite fijar el código de salida con un valor determinado. Su sintaxis es:
exit [n] siendo n un número entero entre 0 y 255.
Por convención el valor que se devuelve es un cero si ha ido bien o un entero
del rango entre 1 y 255 si ha habido algún error. Normalmente, el rango de 126 a 255
se reserva para ser utilizado directamente por el shell o para fines especiales, y los
códigos del rango de 0 a 125 se dejan para ser utilizados por el programa.
En todo guion de shell conviene proporcionar un código de salida significativo,
al menos un 0 (cero) si finaliza bien y un 1 (en general, un valor distinto de cero) si hay
algún error. Esto permitirá que el proceso que ha hecho la llamada pueda verificar
cómo ha ido la ejecución del guion de shell.
Si no se pasa ningún parámetro a exit, el código devuelto por el guion
de shell es el de la última orden ejecutada justo antes del exit. Por ejemplo:
#!/bin/bash
orden_1
...
orden_N
# Termina con el código de salida del orden_N.
exit
El ejemplo anterior, habría sido exactamente igual si se hubiera especificado el
valor del código de retorno de la siguiente manera:
#!/bin/bash
orden_1
...
orden_N
# Termina con el código de salida del orden_N.
exit $?
Análogamente, si un shell script finaliza sin especificar la orden exit, el código
de retorno es el de la última orden ejecutada al shell script.
#!/bin/bash
orden_1
...
orden_N
# Termina con el código de salida del orden_N.
Ejemplo de utilización del orden exit y comprobación de los códigos de salida.
#!/bin/bash
 echo "Esto es una prueba."
# Devolvemos código de salida
exit 115
ANTONIO J. LEÓN DELGADO
49
UD 01: Scripting con bash
Guarda el archivo, dale permisos y ejecútalo.: /salida.sh
La salida del programa es la siguiente:
Esto es una prueba.
Visualiza el código de salida del guion de shell:
echo $?
El valor de salida es 115: lo hemos forzado con la orden exit 115
En la programación de guiones de shell es muy frecuente utilizar el
comando exit para finalizar el programa en cualquier punto sin seguir la norma de la
programación estructurada que dice que un programa debe tener un único punto de
salida. Por ejemplo, un shell script hecho con programación estructurada sería así:
if condicion_error1; then
código=1
else
if condicion_error2; then
código=2
else
if condicion_error3; then
código=3
else
# No hay errores
instrucciones
código=0
fi
fi
fi
exit $código

Pero habitualmente nos encontraremos el guion de shell anterior escrito con


un estilo similar al siguiente:
if condicion_error1; then
exit 1
fi
if condicion_error2; then
exit 2
fi
if condicion_error3; then
exit 3
fi
# No hay errores
instrucciones
exit 0
Aunque los dos programas hacen lo mismo, el primero sigue las normas de la
programación estructurada y tiene un único punto de salida en el último éxito,
mientras que el segundo tiene cuatro posibles puntos de salida, uno para cada orden
de exit.

ANTONIO J. LEÓN DELGADO


50
UD 01: Scripting con bash

Evaluación aritmética y lógica


Al programar casi siempre aparece la necesidad de operar con números, así
como de evaluar el resultado de expresiones aritméticas o lógicas para tomar
decisiones.
En Bash disponemos de algunos métodos para realizar evaluaciones
aritméticas. Veremos dos: la orden let y la construcción doble paréntesis (vista
anteriormente). Para aquellos casos que necesitamos operar con números con
decimales veremos cómo hacerlo utilizando la orden bc.
Otro comando muy utilizado en la programación de guiones de shell para hacer
evaluaciones lógicas es la orden test. Esta orden nos permite evaluar expresiones con
tres tipos de elementos: números enteros, archivos y cadenas de caracteres.

Orden let
La orden let permite a los programas de shell evaluar expresiones aritméticas
con los mismos operadores que en el mecanismo de expansión aritmética y con la
siguiente sintaxis:
let expressión_aritmética
La orden evalúa de izquierda a derecha la expresión y devuelve un código de
salida igual a 0 (verdadero) o 1 (falso). A pesar de que no siempre hay que cerrar la
expresión entre comillas dobles, podemos optar por ponerlas por defecto para evitar
errores.
La expresión aritmética puede constar de números enteros, variables y
operadores del shell. También podemos utilizar paréntesis para cambiar el orden de
preferencia de una expresión. Por ejemplo:

#! /bin/bash
# Definición de variables
x=12
y=2
# Utilización del orden let
let “z = x / y + 1"
echo $z # z vale 7, primero se ha hecho la división
# Alterar precedencia con paréntesis
let "z = x / (y + 1)"
echo $z # z vale 4, primero se ha hecho la suma

Cuando en la expresión utilizamos operadores lógicos o relacionales (! , <=, 


>= , < , > , == , != ) evaluamos el código de salida devuelto por el shell . Por ejemplo:
let “11 <10"
echo $?

ANTONIO J. LEÓN DELGADO


51
UD 01: Scripting con bash
La orden anterior devuelve un 1 indicando que la expresión se ha evaluado
como falsa, ya que 11 no es menor que 10.

Algunas consideraciones a tener en cuenta cuando utilizamos el orden let son:


1.- La orden let no muestra ningún resultado por pantalla. Un error común es
esperar que let devuelva el resultado de una operación por la salida estándar, por
ejemplo, ejecutar let 4+2 y esperar un 6 por pantalla. Para operar con let, hay que
utilizar variables y asignar el resultado a las variables, por ejemplo, let x=4+2.
2.- La expresión de let se escribe sin espacios. Si deseas poner espacios para
separar los operandos de los operadores para que quede más claro, debes poner la
expresión entre comillas dobles. Por ejemplo, let “x = 4 + 2”.
3.- Si en una expresión quieres utilizar paréntesis para alterar el orden de
precedencia de los operadores, debes poner doble comillas para anular el significado
especial de los paréntesis. Por ejemplo, let “x=x/(y+1)”.
4.- Las variables que se utilizan dentro de una expresión pueden ir
referenciadas o no. Por ejemplo, let x=x+2 es el mismo que let x=$x+2.

La construcción doble paréntesis


La construcción doble paréntesis, (()), permite evaluar expresiones aritméticas
de manera equivalente a la orden let pero con la siguiente sintaxis:
((expresión_aritmética))
Por ejemplo:
(( x = x + ( y / 2 ) ))
Es equivalente a:
let "x = x + (y / 2)"

Observación: muchos autores recomiendan utilizar preferentemente los dobles


paréntesis en lugar del orden let.
A menudo encontramos la construcción doble paréntesis formando parte de
bucles while con el estilo del lenguaje de programación C. Por ejemplo:
x=1
while ( ( x < 10 ) ) ; do
...
órdenes
...
(( x++ ))
done

Al igual que con el orden let, las variables utilizadas como operandos en una
expresión pueden ir precedidas del símbolo $ o no. Por ejemplo, las dos expresiones
siguientes son correctas:
(( x = x + 1 )) # Correcto
(( x = $x + 1 )) # Correcto
Pero ten cuidado y no cometas errores como este:
(($x = x + 1)) #Incorrecto !!
ANTONIO J. LEÓN DELGADO
52
UD 01: Scripting con bash

Operaciones con números con decimales


La orden bc es un programa muy potente que incorpora un lenguaje de
programación propio y que permite hacer cálculos con precisión. Esta orden puede ser
de mucha utilidad si necesitamos realizar operaciones con números con decimales.
Si ejecutamos bc a la línea de comandos, nos aparece una información sobre la
versión del programa similar a la siguiente:

El programa queda esperando la entrada de datos de forma interactiva desde el


teclado, por lo que podemos introducir las operaciones que queremos hacer,
pulsar Intro y luego obtenemos el resultado, por ejemplo:
6*3/2obtenemos el siguiente resultado: 9
Para salir de la calculadora tenemos que escribir la palabra quit seguida de la
tecla INTRO. Podemos utilizar bc dentro de un shell script mediante una tubería para
redirigir los datos de entrada al programa. Por ejemplo:
echo "(2 + 3) * 5" | bc
En el ejemplo anterior utilizamos la orden echo para pasar los datos de entrada
a la orden bc (los datos vienen entre comillas dobles). El resultado de la operación nos
sale por pantalla, en este caso un 25.
Para trabajar con números con decimales especificamos la precisión (cantidad
de decimales) con la opción scale. Por ejemplo:
echo "scale = 2; 7 * 5/3" | bc
La orden anterior nos muestra un 11,66 por pantalla, que es el resultado de la
operación que hemos realizado.
Vemos otro ejemplo en el que mostramos el número pi (π) por pantalla
utilizando la opción -l en la llamada de bc para poder usar funciones matemáticas. En
este caso la función a (x), que nos devuelve el arco tangente de x en radianes:

ANTONIO J. LEÓN DELGADO


53
UD 01: Scripting con bash

Orden test
La orden test evalúa expresiones lógicas y genera un código de salida que indica
si la expresión es cierta (código de salida igual a cero) o falsa (código de salida distinto
de cero). Esta orden no escribe nada en la salida estándar. Para determinar el
resultado de la orden test hay que evaluar el valor del código de salida con la
variable “?”.
La sintaxis de la orden test es la siguiente:
test expresión_test
O en forma abreviada:
[ expresión_test ]
Observación: hay que dejar un espacio después del símbolo [ y antes del
símbolo]
En las siguientes tablas puedes ver las expresiones que se pueden evaluar con
test.
Números enteros

Ejemplos
Orden Test

Orden test en su forma abreviada

ANTONIO J. LEÓN DELGADO


54
UD 01: Scripting con bash

Ficheros
Observación: probar alguna de estas opciones para comprobar cómo se
comportan las diferentes opciones. Por ejemplo, las opciones –w, -r y –x se refieren a
los propietarios y grupos propietarios de los ficheros consultados.

Un ejemplo:

Ejemplo

ANTONIO J. LEÓN DELGADO


55
UD 01: Scripting con bash
Cadenas
-n cadena → cierto si la longitud de la cadena es distinta de 0
-z cadena → cierto si la longitud de la cadena es 0
cadena1 = cadena2 → cierto de las cadenas son iguales
cadena1 != cadena2 → cierto si las cadenas son diferentes
cadena → cierto si la cadena de caracteres no es nula
Observación: cuidado con las opciones -n y -z en cadenas cuyo contenido sean
espacios en blanco

Observación:
if [[ $i == A* ]] ; then
….
Está comparado $i y será verdadero si $i empieza por A.
Ojo: doble corchete.

Ejemplos
1.- Usa test para evaluar estas cadenas
$cadena1=”rojo”
4cadena2=”verde”
$test “$cadena1” = “$cadena2”
$echo $?
1
$test “$cadena1” = “rojo”
$echo $?
0

Longitud de una cadena


Se puede obtener con la siguiente sintaxis: echo ${#cadena}

2.- Con la orden test, comprueba si el número 11 es mayor que el número 15.
ANTONIO J. LEÓN DELGADO
56
UD 01: Scripting con bash
test 11 -gt 15
Evalúa el código de salida de la orden que acabas de ejecutar para saber si la
expresión anterior es verdadera (0) o falsa (diferente de 0):
echo $?
1
La salida es un 1, lo que indica que la expresión "11 es mayor que 15" es falsa.
3.- Con la forma abreviada del orden test, comprueba si el número 15 es igual
que el número 15.
[ 15 -eq 15 ]
Evalúa el código de salida de la orden que acaba de ejecutar para saber si la
expresión anterior es verdadera (0) o falsa (diferente de 0):
echo $?
0
La salida es un 0, lo que indica que la expresión "15 es igual que 15" es cierta.

4.- Con la forma abreviada de test, comprueba si la cadena "hola" es igual a la


cadena "HOLA":
[ "Hola" = "HOLA" ]
Evalúa el código de salida para saber si la expresión anterior es verdadera
(cadenas iguales) o falsa (cadenas diferentes):
echo $?
1
La salida es un 1, lo que indica que la expresión es falsa, ya que "hola" en
minúsculas no es igual a "HOLA" en mayúsculas.
5.- Con la orden test, comprueba si existe un archivo llamado /etc/passwd:
test -f /etc/passwd
Evalúa el código de salida de la orden que acaba de ejecutar para saber si la
expresión anterior es verdadera (el archivo existe) o falsa (el archivo no existe):
echo $?
0
La salida es un cero, lo que indica que la expresión es verdadera (el archivo existe).
6.- Asignar a una variable N el valor 15, después con la orden test comprueba si
el valor de N está entre 10 y 20:
N=15
[ $N -gt 10 -a $N -lt 20 ]
echo $?
0
El código de salida es un cero, lo que indica que la expresión es verdadera.
7.- Escribir con la forma abreviada del comando test una comprobación que
diga cierto si el directorio /tmp no existe:
[ ! -d /tmp ]
echo $?
1
El código de salida es un 1, lo que indica que la expresión es falsa, dado que el
directorio /tmp si existe.

ANTONIO J. LEÓN DELGADO


57
UD 01: Scripting con bash
Cuando evaluamos el valor de una variable en una expresión, debemos
asegurarnos de que la variable siempre contiene algún valor, porque si no, la variable
valdrá null y el script nos dará un error. Por ejemplo:
[ $X -eq 3 ]
bash : [ : -eq: esperaba un operador unario
-eq es un operador binario (necesita 2 operandos)
La variable X no tiene ningún valor, por lo tanto el shell ha interpretado [ -eq
3 ] y ha dado un error.
Si trabajamos con cadenas de caracteres, podemos evitar este error poniendo
siempre las variables entre comillas dobles (""). Así nos aseguramos de que la variable
contiene, al menos, el valor null y el shell interpretará la cadena como vacía. Por
ejemplo:
[ "$X" = 3]
En este caso, si X no tiene ningún valor, el shell interpreta [ "" = 3] y no da error.

Observación: el shell interpreta los valores de una expresión de test como


enteros o como cadenas de caracteres según los operadores que utilizamos.

Las expresiones que se utilizan con la orden test pueden ser conectadas


lógicamente utilizando los operadores propios de test -a y -o, pero la manera más
recomendable de hacerlo es utilizar las órdenes de test de manera independiente y
combinarlas con los operadores lógicos && (AND lógico) y || (OR lógico). Por
ejemplo, en lugar de escribir
[ $N -gt 10 -a $N -lt 20 ]
es mejor separar las expresiones y conectarlas así:
[ $N -gt 10 ] && [ $N -lt 20 ]
Pero ten cuidado; los operadores lógicos no pueden ir dentro de los corchetes
de test:
# Operación errónea
[$N -gt 10 && $N -lt 20]

Estructuras de control
ANTONIO J. LEÓN DELGADO
58
UD 01: Scripting con bash
Las estructuras de control (if, case, while, for, etc.) permiten cambiar el flujo
secuencial de un programa en función de la evaluación de unas condiciones y
bifurcarse hacia un lado o hacia otro o repetir la ejecución de unas instrucciones.
A menudo utilizamos la orden test abreviada con corchetes o la construcción
doble paréntesis para evaluar expresiones y tomar decisiones.
Pero ni los corchetes de la orden test ni los paréntesis de la construcción
doble paréntesis forman parte de la sintaxis de ninguna estructura de
control del shell. No lo confundas con otros lenguajes de programación en que la
especificación de condiciones en las estructuras de control debe hacerse entre
paréntesis porque la sintaxis lo requiere.
Estructuras alternativas
Las estructuras alternativas son aquellas que nos permiten ejecutar una parte
del programa en función de si se cumple o no una condición.
Estructura if
La estructura if proporciona un control de flujo basado en el código de retorno
de una orden. La sintaxis es:
if condición; then
orden1
orden2
...
órdenes
fi
El shell ejecuta la orden que establece la condición posterior a if y evalúa el
código de retorno resultante:
 Si el valor del código de retorno es igual a cero, entonces se ejecutan las
órdenes que haya entre las palabras clave then y fi.
 Si el valor del código de retorno es diferente de cero, no se ejecutan las
órdenes que haya entre las palabras clave then y fi y el programa sigue
ejecutando el que haya detrás de la palabra clave fi.
Habitualmente utilizamos las órdenes test y let (o el doble paréntesis
equivalente) para especificar las condiciones. Por ejemplo, el código siguiente
comprueba si existe el fichero /etc/passwd:
if test -f /etc/passwd ; then
echo "El archivo /etc/passwd existe."
fi
O bien con la forma abreviada de test:
if [ -f / etc/passwd ] ; then
echo "El archivo /etc/passwd existe."
fi
Pero la sintaxis de if acepta cualquier orden (todas las órdenes generan un
código de retorno). Por ejemplo:
if $(grep ^root /etc/passwd > /dev/null) ; then
echo "El usuario root existe."
fi
Equivalente:
if `grep ^root /etc/passwd > /dev/null` ; then
echo "El usuario root existe."
ANTONIO J. LEÓN DELGADO
59
UD 01: Scripting con bash
fi
Equivalente:
if grep ^root /etc/passwd > /dev/null ; then
echo "El usuario root existe."
fi
Equivalente:
if (grep ^root /etc/passwd > /dev/null) ; then
echo "El usuario root existe."
fi

Nótese que la estructura if implica una ejecución de la orden que establece la


condición y una evaluación del código de retorno de manera implícita. 
De manera equivalente, se puede ejecutar la orden que establece la condición
antes del if y evaluar el código de retorno de manera explícita, por ejemplo:
test -f /etc/passwd
if [ $? -eq 0 ] ; then
echo "El archivo /etc/passwd existe."
fi
Y también:
grep ^root /etc/passwd > /dev/null
if [ $? -eq 0 ] ; then
echo "El usuario root existe."
fi
Ahora bien, la forma más habitual de utilizar la estructura if es con la
evaluación del código de salida de manera implícita.
Observación: ¿las siguientes sintaxis son equivalentes? Probarlo y sacar
conclusiones.
grep ^root /etc/passwd>/dev/null
`grep ^root /etc/passwd>/dev/null` -->recomendada
$(grep ^root /etc/passwd>/dev/null) -->recomendada
(grep ^root /etc/passwd>/dev/null)
(( grep ^root /etc/passwd>/dev/null )) --> es para expresiones
[ grep ^root /etc/passwd>/dev/null ] --> es para enteros, cadenas y ficheros

Ejemplo de utilización de la estructura de control alternativa if


Hacer un guion de shell que se llame esfich.sh que indique si un nombre dado
como parámetro es un fichero regular.
#!/bin/bash
ANTONIO J. LEÓN DELGADO
60
UD 01: Scripting con bash
# Recibe un parámetro y comprueba si es un archivo.
if [ -f $1 ] ; then
echo "$1 es un archivo."
fi
exit 0
El script anterior se puede mejorar haciendo una comprobación del número de
parámetros recibidos.

#!/bin/bash
# Control del número de parámetros
if [ $# -ne 1 ] ; then
echo "Número de argumentos erróneo."
echo "Uso del programa: $0 nombre"
exit 1
fi
#
# Comprobar si el parámetro es un archivo
if [ -f $1 ] ; then
echo "$1 es un archivo."
fi
exit 0

Estructura if-else
La estructura if-else permite tomar un curso de acción si el código de retorno
de la orden que controla la condición es cero (verdad) y otro curso de acción si el
código de retorno es diferente de cero (falso). La sintaxis es la siguiente:
if condición; then
orden 1
else
orden2
fi
Para ver el funcionamiento hacemos un guion de shell sencillo que pida la
entrada de un número por teclado y que a continuación nos diga si el número leído es
igual a 10 o no:
#!/bin/bash
echo "Introduce un número:"
read X
if [ "$X " –eq 10 ] ; then
echo "El número es igual a 10."
else
echo "El número no es igual a 10."
fi
Si el usuario no introduce nada, el programa daría un aviso.

ANTONIO J. LEÓN DELGADO


61
UD 01: Scripting con bash

Lo evitaríamos, por ejemplo, con el operador que compara cadenas:

En el ejemplo que acabamos de ver hemos utilizado la orden test para


especificar la condición de la estructura condicional, pero también se podría haber
hecho con el orden let o con la construcción doble paréntesis de la siguiente forma:
echo "Introduce un número:"
read X
if (( " $X " == 10 )) ; then
echo "El número es igual a 10."
else
echo "El número no es igual a 10."
fi

Ejemplo de utilización de la estructura de control alternativa if-else.


Hacer un guion de shell que se llame esdir.sh que reciba un argumento y si es
un directorio muestre el mensaje "El contenido del directorio directorio es:" y liste su
ANTONIO J. LEÓN DELGADO
62
UD 01: Scripting con bash
contenido. Si el argumento no es ningún directorio debe dar un mensaje
informativo. Hacer el control del número de argumentos.
#! /bin / bash
# Control del número de parámetros.
if [ $# -ne 1 ] ; then
echo "Número de argumentos erróneo."
echo "Uso del programa: $0 nom_directorio"
exit 1
fi
#
# Comprobar si el parámetro es un directorio.
if [ -d $1 ] ; then
echo "El contenido del directorio $1 es:"
ls $1
else
echo "$1 no es un directorio."
fi
exit 0

Estructura if-elif-else
La estructura if-elif-else se puede utilizar para construir una bifurcación con
múltiples direcciones. La sintaxis es:
if condición 1; then
ANTONIO J. LEÓN DELGADO
63
UD 01: Scripting con bash
orden1
elif condición 2; then
orden2
elif condición 3; then
orden3
...
else
ordenN
fi
Nótese que la manera recomendada de tabular la estructura if-elif-else es
diferente de lo habitual.
Con esta estructura if-else-if, el shell evalúa las expresiones condicionales
empezando por la primera y continuando por la siguiente de forma descendente hasta
encontrar una condición verdadera (código de retorno igual a cero), momento en que
ejecuta la lista de comandos asociada a esta condición y se salta el resto de la
escalera. Si ninguna condición es verdadera se ejecutarán las órdenes asociadas
al else final. Si todas las condiciones son falsas y no hay else final, no hace nada.
En realidad, la estructura if-then-elif no es más que una manera abreviada de
escribir el mismo código con estructuras if-else anidadas, es decir, es equivalente a lo
siguiente:
if condición1; then
ord1
else
if condición2; then
ord2
else
if condicion3; then
ord
else
if
...
else
ord
fi
fi
fi
fi
Fíjate en la forma que adopta el código cuando se implementa con
estructuras if-else y se tabula de la manera habitual. 

Por ejemplo, el programa siguiente lee un número del teclado y muestra por
pantalla si es más pequeño, mayor o igual que 10, utilizando una estructura if-elif-else:

echo "Introduce un número:"


read X
if [ $X -lt 10 ]; then
ANTONIO J. LEÓN DELGADO
64
UD 01: Scripting con bash
echo "El número es menor que 10."
elif [ $X -gt 10 ]; then
echo "El número es mayor que 10."
else
echo "El número es igual a 10."
fi
Se podría haber escrito el mismo código utilizando estructuras if y if-else de la
siguiente forma:
echo "Introduce un número:"
read X
if [ $X -lt 10 ]; then
echo "El número es menor que 10."
else
if [ $X -gt 10 ]; then
echo "El número es mayor que 10."
else
echo "El número es igual a 10."
fi
fi

Si el número de condiciones es elevado, la estructura if-elif-else permite


compactar el código.

Ejemplo de utilización de if-else


Hacer un shell script llamado nota.sh que pida el valor de una nota (un número
entero) y nos diga si la nota es una D (0, 1, 2), una C- (3, 4), una C + (5, 6), una B (7, 8) o
una A (9, 10).
#! /bin / bash
# nota.sh
# Pide el valor de una nota (entero) y dice si es
ANTONIO J. LEÓN DELGADO
65
UD 01: Scripting con bash
# D (0, 1, 2), C- (3 o 4), C + (5 o 6),
# B (7 u 8) o A (9 o 10).
#
echo "¿Qué nota tienes (un entero de 1 a 10)?"
read NOTA
if [ $NOTA -lt 0 ] || [ $NOTA -gt 10 ] ; then
echo "Nota fuera de rango."
elif [ $NOTA -lt 3 ] ; then
echo "Tienes una D."
elif [ $NOTA -lt 5 ] ; then
echo "Tienes una C-."
elif [ $NOTA -lt 7 ] ; then
echo "Tienes una C +."
elif [ $NOTA -lt 9 ] ; then
echo "Tienes una B."
else
echo "Tienes una A."
fi

Estructura case
La estructura case es útil cuando tenemos múltiples bifurcaciones. Su sintaxis
es:
case "$nombre_var " in
patrón 1)
orden1
...
órdenes
ANTONIO J. LEÓN DELGADO
66
UD 01: Scripting con bash
;;
patrón 2)
orden1
...
órdenes
;;
 
...
 
patrones)
orden1
...
órdenes
;;
esac

Se hace una comparación secuencial de la cadena que hay detrás de la palabra


clave case con los patrones. Cuando se encuentra la primera coincidencia ejecuta la
correspondiente lista de comandos. Si no se encuentra ninguna coincidencia no se
ejecuta nada.
Veamos un ejemplo:
echo "Elige una opción de 1 a 3:"
read OPCION
case "$OPCION " in
1) echo "Has elegido la opción 1."
;;
2) echo "Has elegido la opción 2."
;;
3) echo "Has elegido la opción 3."
;;
*) echo "Opción incorrecta."
esac

Nótese el uso del patrón * , que se puede utilizar para indicar cualquier cosa.

Los patrones son cadenas de caracteres y podemos utilizar los mismos


caracteres con significado especial que utilizamos en la generación de nombres de
ficheros:
 *: para indicar coincidencia con cualquier cadena de caracteres, incluido el
carácter null.
 ?: para indicar coincidencia con cualquier carácter simple.
 []: para indicar coincidencia con cualquiera de los caracteres que haya entre los
corchetes. Los caracteres de la lista van seguidos uno detrás del otro, no
separados por comas ni por espacios ni por ningún otro carácter
ANTONIO J. LEÓN DELGADO
67
UD 01: Scripting con bash
delimitador. Se aceptan rangos con el símbolo menos (-).

También se puede utilizar el símbolo | si se quiere coincidencia con más de un


patrón (se interpreta como un o lógico):
case " $nombre_var " in
patrón 1 | patrón 2 | patrón 3 )
orden1
...
;;
patrón 4 | patrón 5 )
 
...
esac

El siguiente ejemplo muestra el uso de patrones con caracteres con significado


especial. Nótese que en algunos casos puede haber más de una manera de expresar un
mismo patrón, por ejemplo, el patrón [0-2] también se podría haber escrito [012] o
bien 0 | 1 | 2.

Ejemplo de utilización de una estructura case y uso de patrones


Hacer el mismo guion de shell notas.sh que ha implementado con un if-else,
pero ahora utilizando una estructura case.
#! /bin/bash
# Pide el valor de una nota (entero) y dice si es
# D (0, 1, 2), C- (3 o 4), C + (5 o 6),
# B (7 u 8) o A (9 o 10).
#
echo "¿Qué nota tienes (entero de 1 a 10)?"
read NOTA
ANTONIO J. LEÓN DELGADO
68
UD 01: Scripting con bash
case "$NOTA" in
[0-2])
echo "Tienes una D."
;;
[34])
echo "Tienes una C-."
;;
[56])
echo "Tienes una C+."
;;
[78])
echo "Tienes una B."
;;
9|10)
echo "Tienes una A."
;;
*)
echo "Nota fuera de rango."
esac

Operadores && y ||
Los operadores de control && (AND) y || (OR) permiten realizar un control de
flujo básico.
Operador &&
El operador && utiliza para ejecutar una orden, y, si tiene éxito (código de
salida igual a cero), ejecutar la próxima orden de la lista. La sintaxis es:
orden1 && orden2
La orden2 ejecuta sí y sólo si la orden1 devuelve un estado de salida de cero
(verdadero). En otras palabras, se ejecuta orden1 si el código de salida es igual a cero,
entonces se ejecuta orden2. Es decir, es equivalente a la utilización de la
estructura if así:
ANTONIO J. LEÓN DELGADO
69
UD 01: Scripting con bash
if orden1; then
orden2
fi

Por ejemplo:
rm/tmp/archivo && echo "Archivo borrado."
La orden echo sólo se ejecuta si la orden rm ejecuta con éxito (con un código de
salida de cero). Si el archivo se elimina correctamente, la orden rm da un código de
salida igual a cero ya continuación se muestra el mensaje "Archivo borrado". Para
evitar los posibles mensajes de error de la orden rm, podemos redirigir la salida de
errores a / dev / null así:
rm/tmp/archivo 2> / dev/null && echo "Archivo borrado."

Ejemplo de utilización del operador &&


Escribir las órdenes necesarias para comprobar si hay un directorio llamado
/tmp/foo y dar un mensaje de error si no existe.
test ! -d /tmp/foo && echo "El directorio no existe."
O con la forma abreviada de test:
[ ! -d /tmp/foo ] && echo "El directorio no existe."

operador ||
El operador ||utiliza para ejecutar una orden, y, si no tiene éxito (código de
salida distinto de cero), ejecutar la próxima orden de la lista. La sintaxis es:
orden1 || orden2
La orden2 ejecuta sí y sólo si el orden1 devuelve un estado de salida diferente
de cero. En otras palabras, se ejecuta orden1, si el código de salida de orden1 es
diferente de cero, entonces se ejecuta orden2. Por ejemplo:
cat / etc/shadow 2> /dev/null || echo "No se pudo abrir el archivo."
La orden cat intentará leer el fichero /etc/shadow y si falla mostrará el mensaje
"No se pudo abrir el archivo".
Ejemplo de utilización del operador OR
Escribir las órdenes necesarias para buscar el nombre de un usuario llamado
"pepe" en /etc/passwd y dar un mensaje si no se encuentra.
grep "^pepe" /etc/passwd || echo "No se encontró pepe en /etc/passwd."

Combinación de los operadores && y ||


La lista de órdenes unidas por operadores && y ||puede ampliarse. A menudo
se utiliza la combinación de los operadores de la siguiente manera:
orden_condición && orden_true || orden_false
Es decir, se ejecuta el orden_condición. Si el código de salida es:
 cierto (0), entonces se ejecuta orden_true.
 falso (distinto de 0), entonces se ejecuta orden_false.
Es decir, es equivalente a la utilización de la estructura if-else así:
if orden_condicion; then
orden_true
else
orden_false
ANTONIO J. LEÓN DELGADO
70
UD 01: Scripting con bash
fi

Ejemplo de utilización de los operadores OR y AND combinados


Escribir las órdenes necesarias para buscar el nombre de un usuario llamado
"pepe" en /etc/passwd y dar mensajes diferentes según si se encuentra o no.
grep "^pepe" /etc/passwd || echo "No se encontró pepe en /etc/passwd."
&& echo "Encontrado pepe en /etc/passwd."

Escribir las órdenes necesarias para asegurarse de que root es quien está
ejecutando el guion de shell y dar un mensaje dependiendo de si lo es o no.
test $(id -u) -eq 0 && echo "Se ejecuta el script con el superusuario root." ||
echo "Sólo el usuario root puede ejecutar este script."

Escribir las órdenes necesarias para comprobar si el fichero /etc/resolv.conf


existe y dar un mensaje si está y otro mensaje si no está.
[ -f /etc/resolv.conf ] && echo "El archivo /etc/resolv.conf existe." || echo "El
archivo /etc/resolv.conf no existe."

Observación: mejor if/else que utilizar operadores && y ||.

Estructuras iterativas
Las estructuras iterativas son aquellas que nos permiten ejecutar varias veces
una parte de código.
Estructura while
La estructura while permite la ejecución repetitiva de unas sentencias siempre
que la orden de control del bucle while sea cierta (código de salida igual a cero). La
sintaxis es:
while condición; do
orden1
orden2
...
ordenn
done
ANTONIO J. LEÓN DELGADO
71
UD 01: Scripting con bash
Normalmente, se utilizan las órdenes test o let para controlar la condición del
bucle, pero podemos utilizar cualquier orden que devuelva un valor.
Veamos un ejemplo sencillo, un bucle que muestra por pantalla los números
del 1 al 10.
Con doble paréntesis:

Con let:

Con test simplificado:

Ejemplo de utilización de la estructura de control while


Hacer un guion de shell que se llame adivina.sh que asigne un número
aleatorio entre 1 y 6 a una variable N y que a continuación nos pida que lo
acertamos. El programa finaliza cuando acertamos el número y nos dice cuántos
intentos hemos necesitado para adivinarlo.
#!/bin/bash
# Juego para adivinar un número entre 1 y 6
# Calculamos un número aleatorio entre 1 y 6
INTENTOS=1
# Módulo (resto) 6 da un número entre 0 y 5
((N=$RANDOM % 6 + 1 ))
echo -n "Adivina un número entre 1 y 6:"
read RESPUESTA
while (( "$RESPUESTA" != $N )) ; do
ANTONIO J. LEÓN DELGADO
72
UD 01: Scripting con bash
echo -n "El número no es $RESPUESTA. Vuelve a intentarlo:"
read RESPUESTA
(( INTENTOS ++ ))
done
echo "Lo has adivinado, era el $N !!"
echo "Has necesitado $INTENTOS intentos."
exit 0

Estructura until
La estructura until es idéntica a while, excepto que la condición es la
complementaria del while. La lista de comandos se ejecuta siempre que la condición
devuelva un código de salida distinto de cero.
Sintaxis:
until condición; do
orden1
orden2
...
done
Por ejemplo:
X=1
until (( X >10 ) ) ; do
echo “X vale $X”
((X = X + 1))
ANTONIO J. LEÓN DELGADO
73
UD 01: Scripting con bash
done

Estructura for
La estructura for permite especificar una lista de valores y ejecutar las
sentencias para cada valor de la lista. La sintaxis de este bucle es la siguiente:
for nombre_var in lista_de_valores; do
orden1
orden2
...
ordenn
done
La lista de valores es una secuencia de valores separados por espacio o
tabulador que se irán asignando a la variable nombre_var en cada iteración del
bucle for. Por tanto, las sentencias que hay entre las palabras reservadas do y done,
se ejecutarán tantas veces como valores haya en la lista de valores.
La construcción clásica de bucle for del shell difiere significativamente de la de
su homólogo C y otros lenguajes de programación.
Por ejemplo, el siguiente es un bucle for que simplemente muestra el valor de
cada uno de los ítems de la lista de valores (muestra los números del 1 al 5):
for X in 1 2 3 4 5; do
echo $X
done

La estructura for es un mecanismo de bucle muy flexible. Se puede construir un


bucle con cualquier lista que se pueda generar. Podemos generar listas fácilmente,
por ejemplo, mediante la lista de parámetros pasados en la llamada al shell script o
mediante la sustitución de órdenes. Así, el ejemplo que acabamos de ver se podría
haber expresado obteniendo la lista de valores con el resultado de ejecutar la
orden seq de la siguiente manera:
for X in $(seq 1 5); do
echo $X
done
Y en caso de que nuestro guion se ejecute con parámetros, por ejemplo, así:
nombre_guion 1 2 3 4 5
El código siguiente tendrá el mismo efecto que los anteriores:
for X in $*; do
echo $X
done
La variable especial $* contiene la lista de parámetros pasados en la llamada
al programa. 
ANTONIO J. LEÓN DELGADO
74
UD 01: Scripting con bash

Ejemplo de utilización de la estructura de control for.


Observación: este ejemplo podemos recordarlo en la UD Automatización de
tareas.
Hacer un guion de shell, ocupación.sh, que calcule la ocupación de disco de
cada directorio de /home. Puedes obtener la lista de estos directorios ejecutando la
orden ls dentro del directorio /home. Para calcular la ocupación del directorio puedes
utilizar la orden du (disk usage):
#!/bin/bash
# ocupacion.sh
# Calcula la ocupación de los directorios de /home
#
cd / home
for DIR in $(ls); do
if [ -d $DIR ] ; then
# Calcular espacio ocupado por el directorio
du -sh $DIR
fi
done

Ejecución:

ANTONIO J. LEÓN DELGADO


75
UD 01: Scripting con bash

Hacer el mismo guion de shell,espacio.sh, pero recibiendo por parámetro el


nombre del o de los directorios de los que queremos conocer el espacio que ocupan en
el disco.
#!/bin/bash
# Nombre: espacio.sh
# Llamada: espacio.sh dir1 dir2 dir3 ...
#
for DIR in $*; do
if [ -d $DIR ] ; then
# Calcular espacio ocupado por el directorio
du -sh $DIR
else
echo "Error: $DIR no existe o no es un directorio."
fi
done

Ejecución:

El shell Bash también permite utilizar la construcción for al estilo del lenguaje


de programación C con una sintaxis como la siguiente:
for ((expr1; expr2; expr3)) ; do
orden1
orden2
ANTONIO J. LEÓN DELGADO
76
UD 01: Scripting con bash
...
ordenn
done

Observación: la sintaxis de for al estilo de C que permite Bash no funciona con


sh (shell bourne).

En este caso las expresiones sólo pueden ser expresiones aritméticas, no


pueden ser cualquier orden y se utilizan con el propósito siguiente:
 expr1: para inicializar el bucle.
 expr2: para comprobar si la lista de órdenes entre do y done se debe ejecutar.
 expr3: para cambiar la condición después de cada iteración del bucle.
Es equivalente a:
((expr1))
while ((expr2)); do
orden1
orden2
...
ordenn
((expr3 ))
done

Por ejemplo, para mostrar los números del 1 al 5:


for ((x = 1 ; x <= 5 ; x++ ))
{
echo $x
}
Vemos otro ejemplo en el que utilizamos bucles for anidados (un bucle for
dentro de otro bucle for):
#! /bin/bash
#
# estrellas.sh
# Ejemplos de bucles for al estilo de C
#
N=10
for (( i=1 ; i <= $N ; i++ ) ); do
for ((j=1 ; j <= i; j ++ ) ); do
echo -n "*"
done
echo
done
for ((y=$N ; y > = 1 ; y-- ))
do
for ((j=1 ; j <= y; j ++ )); do
echo -n "*"
done
echo
ANTONIO J. LEÓN DELGADO
77
UD 01: Scripting con bash
done

ANTONIO J. LEÓN DELGADO


78
UD 01: Scripting con bash

Funciones

ANTONIO J. LEÓN DELGADO


79
UD 01: Scripting con bash
El lenguaje del shell Bash permite definir funciones, aunque con una
implementación algo limitada.
Una función es un bloque de código que implementa un conjunto de
operaciones, una "caja negra" que lleva a cabo una tarea específica. La utilización de
funciones en un shell script es una manera de agrupar una serie de órdenes que
puedan ser ejecutadas posteriormente utilizando un solo nombre.
Consideraremos el uso de funciones en los casos siguientes:
 Cuando el programa de shell es complejo, es recomendable hacer una
programación modular, es decir, agrupar código en funciones o módulos para
estructurar el programa.
 Siempre que tengamos código repetitivo, es decir, una parte de código que se
debe utilizar más de una vez, hay que considerar el uso de una función.

Ficheros de funciones
Hay un montón de scripts en el sistema que utilizan las funciones como una
manera estructurada de agrupar una serie de órdenes. En algunos sistemas Linux, por
ejemplo, se encuentra el archivo /etc/rc.d/init.d/functions, un archivo de definición
de funciones que incluye el principio de todos los scripts de inicio. Usando este
método, las tareas comunes, como la comprobación de si un proceso se ejecuta, iniciar
o detener un demonio, etc., basta escribirlas una vez.
Como administradores del sistema, podemos crear un archivo de funciones, por
ejemplo /usr/local/bin/funciones, con todas las funciones que utilizarás con
frecuencia en tus guiones de shell. Luego, en cada guion que tenga que utilizar las
funciones, simplemente escribes la línea siguiente:
. /usr/local/bin/funciones
No dejes de poner el punto y el espacio (equivalente a la orden source) antes
del nombre del archivo que contiene las funciones para que el shell actual reconozca
las funciones.
La sintaxis para definir una función es:
function nombre_de_la_funcion {
código de la función
}
O bien:
nombre_de_la_funcion ( ) {
código de la función
}
Hay que tener en cuenta las siguientes consideraciones:
 El nombre de la función debe ser único en el shell o script.
 El carácter { de apertura de la función puede estar en la segunda línea.
 La definición de la función debe estar hecha antes de la primera llamada a la
función que haya en el programa.

Ejemplo

ANTONIO J. LEÓN DELGADO


80
UD 01: Scripting con bash

La función invocada, isDir, se encuentra en /usr/local/bin/func_error

Se recomienda hacer todas las definiciones de las funciones necesarias antes


del código principal del guion que se ejecuta, a menos que haya razones concretas
para no hacerlo. Esto da una visión mucho mejor del programa y asegura que todos los

ANTONIO J. LEÓN DELGADO


81
UD 01: Scripting con bash
nombres de las funciones se conocen antes de ser utilizados. Generalmente, la
estructura básica de un guion de shell que implementa funciones es:
#!/bin/bash
 VARIABLES_CONFIGURACIÓN
 DEFINICIÓN_FUNCIONES
 CÓDIGO_PRINCIPAL
Para llamar una función desde el programa de shell, simplemente ponemos el
nombre de la función, al igual que hacemos con cualquier orden. Cuando se llama a la
función, la lista de órdenes relacionadas con el nombre de la función se ejecuta.
#! /bin/bash
# Definición de funciones
saludaUsuario( ) {
echo "Ejecución de la función $FUNCNAME ..."
echo “Hola $USER !!”
}
mostrarFecha( ) {
echo "Ejecución de la función $FUNCNAME ..."
echo “Hoy es $ (date )”

# Programa principal
saludaUsuario
mostrarFecha

Ejecución:

Parámetros a las funciones


Las funciones aceptan parámetros de la misma manera que las órdenes o que
los shell scripts. Para pasar parámetros a una función basta que en el momento de la
llamada a la función especificamos junto al nombre los valores de los parámetros
separados por espacios. Por ejemplo:
ANTONIO J. LEÓN DELGADO
82
UD 01: Scripting con bash
nombre_funcion param1 param2

La función referencia los parámetros recibidos por la posición que ocupan, al


igual que los parámetros posicionales: $1, $2, etc. Todos los parámetros se pasan por
valor, es decir, cuando volvemos de la función el valor de los parámetros no habrá
cambiado.
Ejemplo de función con parámetros
Hacer una función que haga la suma de dos números recibidos por parámetro y
muestre el resultado. El programa principal debe pedir los números por teclado.
#! /bin/bash
# Definición de funciones
suma () {
((TOTAL = $1 + $2))
echo "La suma de $1 y $2 es: $TOTAL "
}
### Programa principal ###
echo -n "Introduce un número:"
read X
echo -n "Introduce otro número:"
read Y
# Llama a la función suma () con parámetros
suma $X $Y

Al ejecutar una función, los parámetros pasados a la función se convierten en


los parámetros posicionales durante la ejecución de la función. Las variables
especiales *, @ y # también se actualizan para reflejar los cambios. La variable
especial 0, que tiene el nombre del guion de shell, no cambia. La variable del Bash
llamada FUNCNAME se establece con el nombre de la función mientras dura la
ejecución de la función.

ANTONIO J. LEÓN DELGADO


83
UD 01: Scripting con bash
Cuando se completa la ejecución de la función, los valores de los parámetros
posicionales y de las variables especiales *, @ y # se restauran los valores que tenían
antes de la ejecución de la función. Por lo tanto, si necesitamos acceder desde una
función a los valores de los parámetros posicionales y de las variables
especiales *, @ y # del programa que llama a la función, tendremos que guardarlos
previamente a la llamada de la función.

Ejemplo de función con parámetros


Un ejemplo típico de función con parámetros es una función para mostrar los
mensajes de error de un guion de shell. Imagínate que estás haciendo un guion de
shell y que tiene múltiples puntos donde el programa da mensajes de error:

if [ -z $DIR ] ; then
echo "$0: especifique el nombre del directorio."
exit 1
fi
if [ ! -d $DIR ] ; then
echo "$0: el directorio no existe."
exit 1
fi

Cuando esto sucede, es mejor que cree una función para este propósito, por
ejemplo:
mostrarError ( ) {
echo "$0:$*"
exit 1
}
...
if [ -z "$DIR" ] ; then
mostraError "especifique el nombre del directorio."
fi
if [ ! -d " $DIR " ] ; then
mostrarError "el directorio no existe."
fi

Nótese que la variable especial $0 ha mantenido el valor dentro de la función y


que la variable $*ha cogido el valor de la lista de parámetros pasados a la función.

Códigos de retorno
Las funciones siempre devuelven un valor al shell que las llama llamado código
de retorno, que es análogo al código de salida de las órdenes. El código de retorno de
una función puede ser especificado de manera explícita con el orden return.
De otro modo, el valor devuelto por la función es el valor del código de salida
de la última orden ejecutada. El código de retorno de la función puede utilizarse en

ANTONIO J. LEÓN DELGADO


84
UD 01: Scripting con bash
el shell script mediante “$?”, del mismo modo que el código de salida de cualquier otra
orden.
Orden return
La orden return interrumpe la ejecución de una función. La sintaxis de la orden
es: return [n]
De manera opcional acepta un número entero como parámetro que es
devuelto al guion de shell que hace la llamada como el código de retorno de la función
y es asignado a la variable $?. Si no indicamos parámetro, el valor devuelto es el valor
del código de retorno de la última orden ejecutada antes del return. La orden return
utilizada fuera del contexto de una función hace lo mismo que el orden exit.

Ejemplo de función con código de retorno


Modifique la función suma.sh porque en lugar de mostrar el resultado de la
suma de los dos números por pantalla, lo devuelva mediante la orden return. En el
programa principal acceda al valor devuelto por la función con la variable $?.
#!/bin/bash
# suma.sh
# Pide dos números por teclado y muestra la suma
# Definición de funciones
suma ( )
{
( ( TOTAL = $1 + $2 ) )
return $TOTAL
}
### Programa principal ###
echo -n "Introduce un número:"
read X
echo -n "Introduce otro número:"
read Y
suma $X $Y
RESULTADO = $?
echo "La suma de $ X y $ Y es: $RESULTADO "

ANTONIO J. LEÓN DELGADO


85
UD 01: Scripting con bash

ANTONIO J. LEÓN DELGADO


86

También podría gustarte