ASO-GISI
La bash es una shell de UNIX/Linux. Una shell es una aplicación que facilita la interacción del usuario con el sistema operativo. Los ordenadores de propósito general y servidores se utilizan y administran generalmente a través de la shell.
Bash está basada en la shell sh (de hecho su nombre procede de “Bourne-Again SHell”, en referencia a Stephen Bourne que fue el creador de sh). Se trata de una shell textual (las hay también de tipo gráfico, como la Windows shell), utilizada por defecto, de muchos tipos de Linux y, hasta hace unos años, era la shell textual de MacOS. Además de bash y sh, existen otras shell para UNIX/Linux como ksh, dash, tsch, zsh, etc.
La bash se ejecuta desde un terminal. Un terminal es un dispositivo, virtual o físico que permite a un operador humano enviar mensajes de texto (generalmente desde un teclado) y recibir información textual de regreso (generalmente por una pantalla; puede incluir también pitidos de “alarma”).
Desde Ubuntu, para abrir una terminal virtual desde la shell gráfica (que por defecto es Gnome Shell) se puede pulsar el icono Terminal o la combinación de teclas CTRL+ ALT+T. Se pueden abrir varios terminales a la vez según sean necesarios, identificándose cada terminal como un dispositivo (virtual) distinto.
Por ejemplo:
Como se ve en la columna PID
, cada terminal está ejecutando una bash (procesos 3261 y 5355). Por otro lado en la columna TTY
(de TeleTYpewriter) se identifica cada uno de los terminales (pts/0
y pts/1
). El identificador pts
viene de Pseudo-Terminal Slave.
Como casi todos los dispositivos en Linux, los terminales (incluidos los virtuales) se manejan con archivos de dispositivos, que cuelgan del directorio /dev
. Por ejemplo, pts/0
se corresponde con /dev/pts/0
:
Como puede ver, los archivos 0
y 1
son de tipo carácter (a diferencia de los asociados a discos, que son de tipo bloque). Pruebe desde el terminal identificado como pts/0
a ejecutar lo siguiente:
Muchas características de la bash son comunes a otras shell de Linux:
Bash se puede utilizar de forma interactiva (con el usuario introduciendo las órdenes por teclado) o no interactiva (en este caso las órdenes suelen darse mediante un script). Se pueden ejecutar órdenes externas de forma síncrona (espera hasta que terminen) o asíncrona (en el caso de que se lancen en segundo plano).
Órdenes internas: Son nativas de la bash. La orden $ help
obtiene un listado de las órdenes internas disponibles.
Órdenes externas: Son las no nativas. Deben de ser llamadas por el interprete de órdenes para ser ejecutadas. Son todas las que se encuentran accesibles desde la variable de entorno $PATH
.
Para averiguar si una orden es interna o externa, además de help
, puede utilizarse type
y where
.
Las órdenes, tanto externas como internas, devuelven un valor al finalizar (exit status), cuyo valor es 0 si todo fue correcto y distinto de 0 si hubo un error. Existen algunas excepciones como grep
(véase el manual).
Nótese que la mayoría de órdenes devuelven por pantalla un texto con un error, cuando este ocurre. Así, el usuario puede actuar al efecto. Sin embargo, en el caso de programar un script, el manejo de errores textuales es más complejo.
El valor de retorno de la última orden queda almacenada en la variable de shell ?
. Para “acceder” al valor de una variable, como veremos más adelante, se precede de $
. Por ejemplo:
Un alias es un nombre alternativo para una orden.
Imagine por ejemplo que le interesa listar los archivos de un directorio en formato largo con el nombre ll
. Puede entonces definir un alias (la bash sustituirá el alias por su definición antes de la ejecución):
Para ver los alias definidos en su terminal, utilice alias
y para eliminar un alias, utilice unalias
.
La declaración de este alias es temporal (hasta que cierre la shell). Sin embargo, para que un usuario tenga permanentemente disponible un alias debe incluirlo en su archivo ~/.bashrc
que define la configuración de la bash del usuario y que se ejecuta cada vez que el usuario inicia un proceso bash.
Si se quiere que esté disponible para todos los usuarios, el administrador debe incluir el alias en el archivo /etc/bash.bashrc
que también se ejecuta al abrir una bash (y lo hace previamente al archivo .bashrc
del usuario).
La bash tiene una serie de caracteres con un significado especial, por ejemplo *
para expansión de nombres de archivo, &
para lanzar órdenes en segundo plano, |
para las tuberías o pipelines, >
para redireccionar la salida, $
para sustitución de variables, #
para comentarios, etc.
Cuando no queremos que se utilice este significado sino que se trate como un carácter “normal”, hay varias posibilidades a utilizar:
\
. Mantiene el literal del carácter que le sigue. Se usa también al final de una línea para continuarla. Ej.:' '
. Preserva el valor literal de lo contenido entre las comillas. Ej.:" "
. Preserva el valor literal de lo contenido entre las comillas excepto para \
y $
. Ej.:Además, la bash proporciona múltiples expansiones que facilitan su utilización. Las principales son:
{ }
~
$
$( )
$(( ))
Se utiliza para generar cadenas. Opcionalmente las cadenas generadas pueden tener un “prefijo” y/o un “sufijo”.
Ej.:
Permite también generar secuencias de caracteres o números, crecientes o decrecientes, con posibilidad de especificar el incremento (por defecto, 1 o -1 según corresponda). En el caso de secuencias de números, permite tamaños fijos completando con ceros (se especifica añadiendo un 0 a la izquierda al límite inferior o superior).
Ej.:
Se utiliza para obtener ciertos nombres de directorio.
Ej.
Nos vamos a centrar en la expansión de variable, aunque todo lo dicho en esta sección es también válido para los parámetros.
En la bash se pueden declarar variables. Ej.:
Los identificadores de las variables distinguen mayúsculas y minúsculas (por tanto mes, Mes y MES son tres identificadores distintos). También, anteponiendo en la declaración readonly
es posible definir constantes.
Ej.:
Precediendo una variable con $
, o escribiendo ${nombre_variable}
, se sustituye por el valor de la variable.
Ej.:
El valor a asignar a las variables es objeto de varias expansiones si las hubiera: tilde, variable y parámetro, sustitución de orden y aritmética (nótese que no se produce expansión de llaves ni de nombres de archivos).
Ej.:
Las variables creadas como se ha indicado aquí se denominan variables de shell y no se exportan a las subshells.
Ej.:
Existen las denominadas variables de entorno que están disponibles una vez definidas para todas las subshells. Para que una variable sea de entorno hay que precederla de export
.
Ej.:
Existen una serie de variables de entorno predefinidas. Las variables de entorno se visualizan con env
. Podemos encontrar entre otras:
Consiste en el reemplazo por la salida de una orden.
Su sintaxis es:$(orden)
.
Ej.:
Evalúa una expresión aritmética entera y sustituye por el resultado. Los operadores, su precedencia, asociatividad y resultados son los mismos que en lenguaje C.
Su sintaxis es: $((expresión))
.
Ej.:
Se denomina globbing en inglés y consiste en el archivos (o directorios) con patrones que contienen caracteres comodines. Estos son:
*
: Cualquier cadena, incluida la vacía?
: Un carácter cualquiera[ ]
: Cualquier carácter incluido entre los corchetes. Se pueden especificar rangos de caracteres con -
.[:alpha:]
, [:digit:]
, [:punct:]
, etc.: Clases de caracteres (letras, dígitos, cualquier signo de puntuación, etc.).[^]
: cualquier carácter no incluido entre los corchetes.Ej.:
Todo proceso tiene abiertos por defecto los siguientes descriptores de archivo (file descriptors):
Las órdenes suelen usar la entrada y salidas estándar, lo que facilita el encadenamiento de órdenes utilizando pipelines.
Además hay varias posibilidades de redireccionar la entrada y salida a un archivo, como se verá a continuación.
n < archivo
: Abre, para lectura, el archivo cuyo descriptor es n. Si no se especifica n, se emplea el descriptor 0, entrada estándar. Ej.:
n > archivo
: Abre, para escritura, el archivo cuyo descriptor es n. Si no se especifica n, es 1, salida estándar. Si fichero no existe, se crea y si existe, se trunca.
Ej.: Muestre todos los archivos que empiecen por a, b, c y d, sin el contenido de los directorios, en el archivo resultado.txt. La ejecución de esta orden no debe mostrar ningún resultado por pantalla
n >> archivo
: Añade contenido a un archivo existente. Si el archivo no existe, se crea. Ej.:
n > &m
(o n >> &m
): Abre para escritura (o añade) el mismo destino que haya en el descriptor m en el descriptor n. Ej.:
Además, como es muy común establecer una redirección de la salida de error estándar a la salida estándar, la sintaxis reducida es & > archivo
. Ej.:
<<ETIQUETA
: La entrada estándar lee del texto indicado hasta que encuentre de nuevo ETIQUETA.
Ej.:
Si se usa <<-ETIQUETA
se ignoran tabuladores al inicio de las líneas, lo que permite, en el caso de los scripts, que el código quede más claro.
<<< cadena
: La entrada estándar lee de la cadena especificada.
Ej.:
Las formas principales que adoptan las órdenes en la bash son:
Secuencia de palabras separadas por espacios, terminados por uno de los operadores de control de la shell (nueva línea, |
, &
, ||
, &&
, etc.). La primera palabra suele ser la orden a ejecutar y el resto los argumentos. Devuelve un exit status que indica el resultado de la orden.
Ej.:
Secuencia de una o más órdenes simples separadas por pipes, esto es |
. La salida estándar de la orden precedente pasa a ser la entrada estándar de la orden siguiente. El exit status es el de la última orden del pipeline.
Ej.:
Secuencia de uno o más pipelines separados por ;
, &
, &&
o ||
y terminadas opcionalmente en ;
, &
o nueva línea. El exit status es el de la última orden ejecutada.
Nota: &&
y ||
asocian por la izquierda.
(lista)
o en la misma shell con la sintaxis { lista;}
.Ej.: (importante espacios y ;
en { lista; }
)
Asimismo, se revisarán en los siguientes capítulos:
Los comportamientos de algunas de las expansiones se pueden configurar con la orden shopt
(SHell OPTions). Por ejemplo, obsérvese el comportamiento para el uso de caracteres comodín:
Este comportamiento es debido a que, por defecto, la opción nullglob
de shopt
esta desactivada. Esto significa que, si no es posible expandir la cadena a uno o más nombres de fichero, se usa la cadena tal cual. Otra posibilidad sería que, si no es posible expandir, se devolviera “vacío”, con lo que el comportamiento sería distinto. Por ejemplo:
Si se quiere modificar permanentemente esta u otra opción de la shell, habría que colocar la instrucción correspondiente en el archivo ~/.bashrc
del usuario (o en el archivo /etc/bash.bashrc
, si se deseara para todos los usuarios).
De forma resumida, el ciclo de ejecución de una orden realiza los siguientes pasos:
Lee la orden desde el teclado o desde un archivo (shell script).
Una orden puede ocupar más de una línea, usando el continuador, \
, al final de cada línea.
Divide la entrada en tokens (“palabras” y “operadores”) teniendo en cuenta el entrecomillado. En este paso se realiza también la expansión de los alias.
Analiza los tokens diferenciando órdenes simples y compuestas.
Las órdenes compuestas son estructuras de control del lenguaje de programación de la bash (for
, if
, etc.) y varias formas de agrupamiento que veremos más adelante.
Realiza varias expansiones de la shell, separando los tokens expandidos en listas de nombres de archivos y órdenes/argumentos.
Realiza las redirecciones de la entrada/salida que hubiera (eliminando de la lista de tokens los operadores y operandos relativos a las redirecciones).
Ejecuta la orden y, si la orden no ha sido lanzada en segundo plano, espera a que finalice recogiendo su valor de retorno (exit status) al terminar.
Repite (o termina shell, si la orden era exit
).
Imagíne que tiene:
De manera aproximada, la última línea se procesaría como sigue:
ls, -l, *.txt, >, /tmp/resultado.txt
.ls
es un alias de ls --color=auto
. Por tanto, el token ls
se sustituiría por los tokens ls
, --color=auto
.*.txt
se expande a f1.txt f2.txt f3.txt
, considerando la orden y sus argumentos ($ ls -l f1.txt f2.txt f3.txt
)./tmp/resultado.txt
y se elimina de la lista de tokens >
y /tmp/resultado.txt
.ls
es una orden externa, la bash realiza una llamada al sistema operativo, a la función exec
, y almacena el valor resultante (exit status) en la variable ?
.listadir
para la orden ls
. Pruébalo con alguna opción del ls
¿Funciona?-h
(de human) de la orden ls
hace que los tamaños de archivo aparezcan con un múltiplo adecuado, para que sean más fáciles de leer. Se quiere que, a veces, el ´ls´ lleve implícito -h
y otras no.HUMANO
que tendrá el valor -lh
y declare un alias de ls
con dicha variable.HUMANO
y vuelva a ejecutar dicho alias. ¿Se obtiene el mismo resultado en ambos casos?prueba.txt
de directorio $HOME
al directorio /tmp
. En caso de no existir, no debería aparecer ningún error y se crearía un archivo en /tmp
que contendría el calendario del mes actual (cal
), además del resultado de ejecutar ls
en el directorio de trabajo.touch
) cuyos cuatro primeros dígitos sean el año que viene (tiene que funcionar independientemente del año en curso) y los dos siguientes el mes.grep
para comprobar si en la ruta completa del directorio actual de trabajo aparece la letra s
. En caso de no aparecer, no muestra nada./tmp/aso
, se escribirá exclusivamente un mensaje del estilo “El directorio /tmp/aso contiene la letra s” y si no aparece, no escribe nada.echo
a grep
y otra usando “aquí cadena” como entrada de grep
.