Scripts básicos para bash

Indicadores de Logros

Lectura: Scripts para bash

Un script para bash es un archivo tipo texto, cuyas líneas tienen comandos que son ejecutados (interpretados) por bash. Para lograr que el intérprete de comandos intérprete las líneas de un archivo puede:

En este capítulo presentamos algunas facilidades que bash brinda y que resultan muy útiles para escribir scripts. Antes se presentará como ejecuta bash una orden (lo cual en particulars explica porque la primera línea de un script para bash debería ser #!/bin/bash).

Dado que las facilidades que presentaremos no son exclusivas de scripts sino de bash, puede hacer experimentos desde el intérprete de comandos mientras lee.

Ejecución de un comando en bash

Para determinar cual es la orden bash realiza varias acciones:

  1. Primero realiza ciertas expansiones a la línea de comando, e identifica la orden, los parámetros y eventualmente las variables de ambiente que se den junto con la orden (en esta sección se estudiarán las expansiones y variables de ambiente).

  2. Si la orden va precedida de una ruta ---se trata del nombre completo de un archivo--- y el archivo existe y es ejecutable, bash lo trata como un programa y lo carga a memoria para ejecutarlo. Si la ruta no conduce a un archivo ejecutable bash presenta un mensaje de error.

  3. Si la orden no va precedida de una ruta, busca un alias que pueda corresponder con la orden y si lo encuentra lo remplaza por su valor (más adelante en esta sección explicaremos como manejar alias).

  4. Si la orden no esta precedida de una ruta y no es un alias, determina si se trata de un comando interno de bash (como fg o bg) y en caso de serlo realiza la acción correspondiente.

  5. Si la orden no está precedida de una ruta, no es un alias y no es un comando interno busca en varios directorios un archivo ejecutable con el nombre dado y si lo encuentra en alguno lo carga a memoria para ejecutarlo (el orden y los directorios donde busca se especifican en la variable de ambiente PATH que se explicará en esta sección).

  6. En otro caso bash presenta un mensaje de error.

En caso de que la orden corresponda a un archivo ejecutable (bien porque se dio la ruta completa o bien porque existe un archivo en un directorio de PATH), bash determinará como ejecutarlo:

  1. Si el archivo es tipo texto y comienza con la cadena "#!" seguida del nombre de un programa, bash emplea tal programa como intérprete del archivo. Como parámetros para el intérprete emplea los que estén en la primera línea del archivo, seguidos del nombre del archivo y a continuación otros parámetros que el usuario hubiera dado desde la línea de comandos.

  2. Si el archivo es tipo texto pero no comienza con #!, lo interpreta con el programa bash [1]. Es decir lo trata como un script para el intérprete de comandos.

  3. Si el archivo es binario lo envía al kernel para su ejecución. El kernel podrá ejecutarlo si está en un formato reconocido (e.g el ejecutable producido por un compilador en un formato reconocido por Linux ---ELF, a.out).

Con estos caracteres comienza un archivo tipo texto que debe ser interpretado cuando el usuario solicita su ejecución, a continuación de ellos debe estar la ruta del intérprete.

Ambiente y variables de ambiente

Una variable es un nombre al cual se le puede asociar un valor, tal valor puede cambiar durante la ejecución de un programa ---es 'variable'. Cada programa (incluyendo al intérprete de comandos) se inicia en un ambiente el cual consta de variables ---variables de ambiente--- que hereda del programa que lo inició y que pueden tener un significado especial para el programa. Las variables de ambiente que un programa usa se especifican en la página del manual del programa.

Palabras empleadas por bash y otros programas, que tienen asociado un valor. Algunos ejemplos son PATH, MAIL y USER (su respuesta debe estar en minúsculas).

Para asignar un valor a una variable de ambiente y crearla si no existe, teclee el nombre de la variable, en seguida el carácter '=' y después el valor. El valor puede constar de letras, símbolos o números, pero tenga en cuenta que hay algunos caracteres con significado especial y que es mejor evitar en sus primeros experimentos: $, {, }, `, ' (podrá escribirlos precedidos de \ o encerrando el valor a asignar entre apostrofes). Por ejemplo para asignar el valor /home a la variable DIR:


DIR=/home

Estando en bash, cone esta orden puede asignar el valor 1 a la variable de ambiente VAR.

Después de asignar una variable, puede emplear el valor asociado a la misma en comandos que de al intérprete de comandos, para hacerlo emplee el nombre de la variable precedido del carácter '$'. Por ejemplo ls -l $DIR listará los archivos de la ruta asociada a la variable DIR.

Para emplear el valor de una variable de ambiente en sus scripts para el intérprete de comandos, debe anteponer este caracter al nombre de la variable.

En bash puede emplear el comando echo para enviar a salida estándar una cadena (por ejemplo echo tarea > porhacer.txt dejar en el archivo porhacer.txt una línea con la palabra tarea), esto puede usarse para examinar el valor de una variable de ambiente, e.g. echo $DIR presentará el valor de la variable DIR.

Orden para el intérprete de comandos que le permitirá examinar el valor de la variable de ambiente VAR.

Puede examinar las variables de ambiente de bash con el comando set. Algunas de las variables que verá son: USER y USERNAME cuyo valor es el login del usuario; UID con el número que identifica al usuario; TERM mantiene el nombre de la terminal que está usando (ver Lectura Configuración de una sesión); SHELL la ruta y nombre del intérprete de comandos; PWD el nombre del directorio de trabajo; $HOME el nombre del directorio personal del usuario; PS1 y PS2 indican a bash como presentar prompts (see Lectura Configuración de una sesión); PATH es la ruta de directorios donde bash busca archivos ejecutables, se separan unas rutas de otras con el caracter ':'; OSTYPE el tipo de sistema operativo; MAILCHECK la frecuencia en segundos con la que bash debe revisar si ha llegado un nuevo correo a la cola de correos especificada en la variable MAIL (por defecto la del usuario); LS_COLORS colores que emplea el programa ls; LINES y COLUMNS indican la cantidad de filas y columnas de la terminal que está usando; LANG, LANGUAGE y otras variables que comienzan con el prefijo LC_ especifican el idioma en el que los programas deben interactuar con el usuario (ver Lectura Configuración de una sesión); HOSTNAME es el nombre del sistema; HISTFILE mantiene el nombre del archivo con la historia de comandos, su tamaño lo limitan HISTFILESIZE y HISTSIZE; DISPLAY mantiene la dirección del servidor X-Window (see Lectura Servicios de la Intranet).

Nombre de la variable de ambiente que mantiene la dirección del servidor X-Window.

Cuando se inicia un programa desde bash, el ambiente que tendrá constará de las variables que estén marcadas como exportables y de otras variables que se especifiquen al comienzo del comando (pueden separarse unas de otras con espacios y si el valor de alguna variable debe tener espacios puede encerrar el valor completo entre comillas), por ejemplo para iniciar el programa man en un ambiente con las variable LANGUAGE y LANG en el valor DE_de:


LANG=de_DE LANGUAGE=de_DE man man

Orden que debe dar a bash para iniciar el programa miprograma con un ambiente en el que la variable de ambiente VAR tenga el valor 1.

Para exportar una variable y lograr así que forme parte del ambiente de procesos creados por su sesión, puede emplear bien declare -x VAR o export VAR, empleando el nombre de la variable que desea exportar en lugar de VAR. Empleando sólo export o sólo declare -x puede ver los nombres y valores de variables exportables.

Orden que debe dar en un script para el intérprete de comandos, para exportar la variable VAR al ambiente del proceso que inicie el script.

En un script puede emplear ciertas variables especiales ($1, $2, ...) para referenciar los parámetros que el usuario empleó al iniciarlo. $1 tendrá el valor del primer paramétro, $2 del segundo y así sucesivamente. El siguiente script ejemplifica su uso:


#!/bin/bash

echo "Creando $2.tar.gz de $1"

mkdir $2
cp -rf $1/* $2
tar cvf $2.tar
gzip $2.tar

Este script recibe dos parámetros, el primero es una ruta y el segundo el nombre de un archivo. Si el nombre del script es comp y tiene permiso de ejecución podría usarse para crear un paquete comprimido d.tar.gz con el contenido del directorio ~/mand con:


./comp ~/mand d

Note que en el ejemplo indicamos la ruta completa del archivo comp, suponiendo que es ejecutado desde el mismo directorio donde se encuentra. Si la ruta donde está el archivo está en la variable PATH, no es necesario especificar la ruta.

En un script para el intérprete de comandos se usa para referenciar el primer parámetro que el usuario pase al ejecutarlo.

Expansiones

bash trata algunos caracteres de forma especial: ' " { } $. Al asignar una variable o iniciar un comando bash "expande" estos caracteres y su contexto de varias formas :

Referencia la cantidad de parámetros que un script para bash recibe.

Referencia todos los parámetros que un script para bash recibe.

Cuando la ejecución en primer plano de un comando termina, esta "variable" del intérprete de comandos referencia el número retornado, es 0 sólo si la operación fue exitosa.

Referencia las opciones que un usuario pasa a un script cuando lo ejecuta.

Referencia la identificación del proceso del intérprete de comandos desde el que se ejecuta un script.

Referencia la identificación del proceso del último comando ejecutado en segundo plano.

En un script, referencia el nombre de script o shell que el usuario emplea para ejecutarlo.

En una orden para bash, una cadena puede encerrarse entre dos caracteres como este, por ejemplo para incluir espacios. Al encerrarla entre caracteres como este, se realizan expansiones excepto expansión de corchetes y de nombres de archivos.

Secuencia que representa el caracter fin de línea en scripts para el intérprete de comandos.

Secuencia que indica borrar el caracter de la izquierda en scripts para el intérprete de comandos.

En el intérprete de comandos puede usarse esta secuencia para producir el caracter '$'.

En el intérprete de comandos puede usarse esta secuencia para producir el caracter tabulador.

En una orden para bash, una cadena puede encerrarse entre dos caracteres como este para evitar expansiones, por ejemplo para incluir espacios y símbolos reservados de bash como el caracter '$'.

En bash este caracter expande a todos los archivos de un directorio ---o a * si no hay archivos en el directorio.

En bash expande al directorio del usuario.

Cadena que bash presenta, si en un directorio hay archivos con nombres: g1,g10,g3,grupo,texto y desde ese directorio se ejecuta la orden echo g?

$var - Expansión de variables o parámetros

El caracter '$' es empleado para distinguir variables o bien parámetros de un script. Las variables son remplazadas por su valor, por ejemplo echo $PATH presentará el contenido de la variable PATH. En un script los parámetros se referencian con números, $1 es el primero, $2 el segundo y así sucesivamente. Otros nombre especiales en un script son:

$#

Es remplazado por la cantidad de parámetros que el script recibe.

$*

Que se expande a todos los parámetros que el script haya recibido, un parámetro se separa de otro con el valor de la variable IFS que normalmente es un espacio.

$?

Todo programa al terminar debe retornar un número al sistema operativo, por convención 0 significa operacióne exitosa y números diferente representan errores. $? se expande al número retornado por el último programa ejecutado en primer plano. Un script puede retornar un 3 en lugar de 0 con exit 3

$-

Opciones que se pasaron al script durante su ejecución.

$$

Identificación del proceso del intérprete de comandos.

$!

Identificación del proceso del último comando que se ejecutó en segundo plano.

$0

Nombre del script o del shell.

"texto" - citas

Cuando un conjunto de caracteres (incluyendo espacios), se encierra entre comillas, bash los trata como una sola cadena. Esto es útil por ejemplo cuando el nombre de un directorio o archivo tiene espacios, e.g. cd "los amigos". Otra forma de representar el caracter espacio es con el caracter '\' seguido de un espacio, así el efecto del ejemplo anterior también podría lograrse con cd los\ amigos. Hay otros caractéres que pueden representarse con ayuda de '\', por ejemplo:

Cadena que bash produce al ejecutar: echo a{a,e}

\n

Representa el caracter fin de línea.

\b

Caracter para borrar a la izquierda.

\\ \{ \} \$

Representan los caracteres '\' '{' '}' y '$' respectivamente. Esto es útil para producir estos caracteres sin que bash trate de interpretarlos --estos son caracteres para hacer expansiones.

\a

Caracter para emitir un sonido.

\t

Caracter tabulador (como la tecla Tab).

{letras} - expansión de corchetes

Una cadena que contenga { letra1, letra2, ... } será expandida a varias cadenas similares a la inicial pero la posición de {letras1, letra2, ...} será remplazada por cada una de las letras. Por ejemplo ls /home/pepe/sal{a,e}n se expandirá a ls /home/pepe/salan /home/pepe/salen. Esta expansión es la primera que se realiza cuando hay varias en un mismo comando, y sólo surge efecto si está fuera de comillas o apóstrofes.

`comando` o $(comando) - sustitución de comandos

Un comando encerrado entre apóstrofes invertidos (i.e `comando`) o entre las cadenas "$(" y ")", será expandido al resultado que tal comando envíe a salida estándar cuando es ejecutado. Por ejemplo


TEXTOS=`ls *.txt`

asignará a la variable TEXTOS los nombres de los documentos tipo texto (i.e el resultado de ls *.txt).


ls $(cat rutas.txt)

presentará los archivos de los directorios que estén en el archivo rutas.txt.

Que valor queda almacenado en la variable a tras ejecutar las siguientes ordenes en bash: echo "un ejemplo" > arch.tmp ; a=`cat arch.tmp`

'texto' - citas

Un texto que se encierra entre apóstrofes no es expandido. Esto es útil cuando se requiere una cadena que tiene algunos caracteres reservados para expansiones. Por ejemplo


N=10
echo '$N' es $N

enviará a salida estándar $N es 10

$((expresión)) - expansión aritmética

Una expresión aritmética [2] será evaluada cuando se encierre entre $(( y )), por ejemplo:


echo "1+2 es $((1+2))"

Resultado produce la siguiente orden para bash: echo "2*2=$((2*2))"

*, ?, ~ [letras] [rango(s)]- Expansión de rutas

Algunos caracteres y secuencias son expandidos a nombres de archivos. A continuación se presentan con ejemplos:

  • echo *, presentará todos los nombres de archivos del directorio de trabajo ---porque * expande a todos estos y echo los presenta---. En caso de que no haya archivos el caracter * no será expandido y será mostrado por echo.

  • echo datos?.gnumeric se expandirá a todos los nombres de archivos que comiencen por datos seguidos de un caracter arbitrario a su vez seguido de la cadena .gnumeric.

  • echo ~/.*errors presentará todos los nombres de archivos de configuración que estén en el directorio del usuario y que terminen con errors.

  • rm *[cho] eliminará todos los archivos que terminen con una de las letras o, h o c.

  • echo [0-9][a-z]* presentará todos los archivos que comiencen con un dígito seguido de una letra.

Cadena que bash presenta, si en un directorio hay archivos con nombres: g1,g10,g3,grupo,texto y desde ese directorio se ejecuta la orden echo *[o3]

Cadena que bash presenta, si en un directorio hay archivos con nombres: g1,g10,g3,grupo,texto y desde ese directorio se ejecuta la orden echo [a-z][a-z]*

Comandos y programas útiles al hacer scripts

En un script puede emplear cualquier programa o comando, junto con redireccionamiento, procesos y control de tareas. bash ignora comandos que comienzan con el caracter '#'. Esto es útil para agregar comentarios explicativos a los scripts.

Caracter empleado al comienzo de líneas en scripts para bash que indica que la línea entera es un comentario.

A continuación introducimos algunos comandos de bash y programas útiles al hacer scripts.

Comando internto de bash que lee una cadena de la entrada estándar y la asigna a una variable de ambiente (o en su defecto a la variable REPLY).

Comando interno de bash que permite crear alias para ciertos comandos.

Comando interno de bash que permite eliminar alias creados con el comando alias.

Programa que recibe el nombre de un archivo y retorna la parte que corresponde al directorio.

Programa que recibe el nombre de un archivo y retorna la parte que corresponde al nombre sin directorio.

Programa que retorna el nombre de la máquina en la que se ejecuta.

Programa que retorna el nombre del usuario que lo ejecuta.

Programa que retorna identificación y grupo(s) del usuario que lo ejecuta.

Comando interno de bash que permite cambiar atributos en variables de ambiente.

read

Lee una línea de entrada estándar y asigna las palabras a las variables que sigan al comando read. Puede especificarse un mensaje que se presentará como prompt antes de empezar a leer con la opción -p mensaje. El siguiente ejemplo lee dos palabras en las variables NOMBRE y APELLIDO:


read -p "Teclee nombre y apellido: " NOMBRE APELLIDO

Si la línea leída tiene más palabras que la cantidad de variables, el resto serán ignoradas. Si tiene menos palabras, las primeras variables (de izquierda a derecha) serán empleadas y el resto quedarán en blanco (i.e con la cadena vacía ""). En caso de que no se den nombres de variables, la línea leída quedará en la variable REPLY.

alias

Con este comando puede definir alias para comandos, después de definir un alias bash lo remplazará por el comando completo. Por ejemplo un alias para abreviar ls -l puede ser ll, que se definiría como:


alias ll="ls -l" 
Puede listar los alias definidos con la opción -p del comando alias.

unalias

Permite eliminar alias creados con el comando alias, por ejemplo para eliminar el alias ll se emplea:


unalias ll

Si se emplea la opción -a, el comando unalias elimina todos los alias definidos.

dirname

Recibe como primer parámetro el nombre completo de una archivo, incluyendo su ruta y envía a salida estándar sólo la ruta. Por ejemplo:


dirname /usr/doc/xterm/README.Debian

presenta en salida estándar /usr/doc/xterm.

basename

Análogo a dirname, pero envía a salida estándar el nombre del archivo.

hostname

Envía a salida estándar el nombre del computador en la red, esto también puede verse en la variable de ambiente HOSTNAME. Con la opción -f, el programa hostname presenta el nombre completo, con la opción -i la dirección IP, con la opción -d el dominio DNS, con la opción -a presenta nombres alternos de su máquina y con la opción -y el dominio NIS ---este programa extrae parte de la información del archivo /etc/hosts y de /etc/hostname.

whoami

Retorna el nombre del usuario que lo ejecuta. También puede verse en la variable de ambiente USER.

id

Retorna nombre y número del usuario que lo ejecuta, así como nombre y número del grupo o grupos a los que pertenezca. Con la opción -g sólo retorna el número del grupo principal, con -u retorna sólo el número del grupo principal, con -G retorna los números de todos los grupos a los que pertenece. Estas opciones pueden seguirse de n para presentar nombres en lugar de números.

declare

Las variables de ambiente tienen atributos que pueden examinarse o cambiarse con este comando interno de bash. Los atributos pueden ser sólo lectura (opción -r), exportable (opción -x) y variable entera (opción -i). Una variable de sólo lectura no puede ser modificada, y una variable entera sólo puede asociarse con números. [3]. Para examinar las variables que tengan un cierto atributo se emplea declare seguido del código de la opción, por ejemplo para examinar las variables con atributo de sólo lectura declare -r. Para cambiar un atributo a una variable se emplea la opción (precedida de - o + para activar y desactivar) seguida del nombre de la variable, por ejemplo declare -i N pone el atributo de variable entera a N.

Lecturas recomendadas: Scripts básicos para bash

Ejercicios: Scripts básicos para bash

1. Cree un script que sea interpretado por bash, que al ejecutarse busque entre sus directorios, todos los archivos, ordene los nombres alfabéticamente y le envíe un correo con esa información. Ayuda: Puede emplear la opción -R de ls para listar subdirectorios, y la opción -u de sort.
2. La variable de ambiente HOME contiene el nombre de su directorio personal. Comprúebelo y después empleela para cambiarse a su directorio. Después cambie esta variable y explique que ocurre con el comodín '~'.
3. El programa man presenta páginas del manual, empleando el programa especificado en la variable de ambiente PAGER o en su defecto con el programa less (ver Lectura bash y el juego de herramientas). Modifique y exporte la variable PAGER para que man emplee el programa more para presentar información y pruebe el cambio.
4. ¿Qué hace el comando echo `ls` ? (note que se usan apóstrofes invertidos).
5. ¿Qué hace el comando N=6 echo "'1+$N' = $((1+$N))" ? Después de dar su respuesta, compruébela empleando un intérprete de comandos.
6. Haga un script que cada 90 minutos presente el mensaje "Hacer tareas". Ayuda: source.
7. Cree un script que al ser ejecutado, cree un archivo script2. El archivo script2 creado debe poner la variable PATH en el valor que tenga cuando el primer script sea ejecutado, añadir a tal variable la ruta /opt/bin y debe crear un alías que cuando se ejecute muestre el contenido de la variable PATH (no olvide cambiar el modo del archivo script2 generado para que sea ejecutable).
8. Haga un script que después de ejecutarse pida al usuario 2 números y después presente la suma, la resta, el producto y la división.

1. Cree un script que sea interpretado por bash, que al ejecutarse busque entre sus directorios, todos los archivos, ordene los nombres alfabéticamente y le envíe un correo con esa información. Ayuda: Puede emplear la opción -R de ls para listar subdirectorios, y la opción -u de sort.

 
#!/bin/bash 
cd $HOME 
ls -R | sort -u | mail $USERNAME@$HOSTNAME 

2. La variable de ambiente HOME contiene el nombre de su directorio personal. Comprúebelo y después empleela para cambiarse a su directorio. Después cambie esta variable y explique que ocurre con el comodín '~'.

echo $HOME; cd $HOME El comodin ~ es remplazado por el valor de la variable $HOME.

3. El programa man presenta páginas del manual, empleando el programa especificado en la variable de ambiente PAGER o en su defecto con el programa less (ver Lectura bash y el juego de herramientas). Modifique y exporte la variable PAGER para que man emplee el programa more para presentar información y pruebe el cambio.

 
export PAGER=more 
man man

4. ¿Qué hace el comando echo `ls` ? (note que se usan apóstrofes invertidos).

Ejecuta el comando ls y el resultado del mismo como cadena lo presenta con el comando echo

5. ¿Qué hace el comando N=6 echo "'1+$N' = $((1+$N))" ? Después de dar su respuesta, compruébela empleando un intérprete de comandos.

Presenta 1+$N = 7.

6. Haga un script que cada 90 minutos presente el mensaje "Hacer tareas". Ayuda: source.

El archivo puede ser recuerda.sh, su contenido:


#!/bin/bash 
sleep 90m 
echo "Hacer tareas"
después ejecutarlo.

7. Cree un script que al ser ejecutado, cree un archivo script2. El archivo script2 creado debe poner la variable PATH en el valor que tenga cuando el primer script sea ejecutado, añadir a tal variable la ruta /opt/bin y debe crear un alías que cuando se ejecute muestre el contenido de la variable PATH (no olvide cambiar el modo del archivo script2 generado para que sea ejecutable).

 
#!/bin/bash 
echo 'PATH=$PATH:/opt/bin' > script2 
echo 'alias sp="echo $PATH"' >> script2 
chmod +x script2

8. Haga un script que después de ejecutarse pida al usuario 2 números y después presente la suma, la resta, el producto y la división.

 
#!/bin/bash 
read -p "Numeros: " A B 
echo $((A+B)) $((A-B)) $((A*B)) $((A/B))

Notas

[1]

En rigor lo interpreta con /bin/sh pero en Debian /bin/sh es un enlace a /bin/bash

[2]

En la expresión pueden emplearse los operadores - (unario y binario), + (unario y binario), ** para elevar a una potencia, * para multiplicar, / para divir, % para obtener residuo de una división. Hay operadores que operan a nivel de bits (representación binaria de los números que operan): ~ para negar bits, & para hacer "Y" entre bits de operandos y | para efectuar "O". También pueden emplearse valores booleanos --- 0 se interpreta como falso y 1 como verdadero---, pueden compararse números con los operadores >, <, <= (menor o igual), >= (mayor o igual), == (igualdad), != (diferentes) y pueden operarse booleanos con ! para negar, && como conjunción (Y) y || como disyunción. Los números pueden escribirse en decimal, o en otras bases empleando como prefijo del número la base seguida del caracter '#' (también puede escribirse números en octal iniciándolos con 0 o en hexadecimal iniciándolos con 0x).

[3]

Otros atributos pueden ser "función" -f y arreglo -a