Una parte importante de aprender a trabajar con Ubuntu, y con las distribuciones de Linux en general, implica ganar destreza en el trabajo en el entorno del shell. Mientras que los entornos gráficos de escritorio como GNOME incluidos en Linux proporcionan una interfaz amigable con el sistema operativo, en la práctica el entorno shell proporciona capacidades, flexibilidad y automatización mucho mayores que las que se pueden lograr utilizando herramientas gráficas de escritorio. El entorno shell también proporciona un medio para interactuar con el sistema operativo cuando un entorno de escritorio no está disponible; algo común cuando se trabaja con un sistema operativo basado en un servidor como Ubuntu o un sistema dañado que no arranca completamente.
El objetivo de este capítulo, por lo tanto, es proporcionar una visión general del entorno de shell por defecto en Ubuntu (específicamente el shell Bash).
1.1 ¿Qué es un Shell?
El shell es un entorno interactivo de interpretación de comandos dentro del cual los comandos pueden ser escritos en un prompt o introducidos en un archivo en forma de script y ejecutados. Los orígenes del shell se remontan a los primeros días del sistema operativo UNIX. De hecho, en los primeros días de Linux, antes de la introducción de los escritorios gráficos, el shell era la única forma de que un usuario interactuara con el sistema operativo.
A lo largo de los años se han desarrollado diversos entornos de shell. El primer shell ampliamente utilizado fue el shell Bourne, escrito por Stephen Bourne en los laboratorios Bell.
Otra de las primeras creaciones fue el shell C, que compartía algunas similitudes sintácticas con el lenguaje de programación C e introducía mejoras de usabilidad como la edición de la línea de comandos y el historial.
El intérprete de comandos Korn (desarrollado por David Korn en los Laboratorios Bell) se basa en características proporcionadas por el intérprete de comandos Bourne y el intérprete de comandos C.
El intérprete de comandos por defecto en Ubuntu es el intérprete de comandos Bash (abreviatura de Bourne Again SHell). Este intérprete de comandos, que comenzó su vida como una versión de código abierto del intérprete de comandos Bourne, fue desarrollado para el Proyecto GNU por Brian Fox y se basa en las características proporcionadas por el intérprete de comandos Bourne y el intérprete de comandos C.
1.2 Cómo acceder al intérprete de comandos
Desde el entorno de escritorio GNOME, se puede acceder al intérprete de comandos desde una ventana de Terminal seleccionando la opción Actividades en la barra superior, introduciendo Terminal en la barra de búsqueda y haciendo clic en el icono de Terminal.
Cuando se inicia la sesión de forma remota en un servidor de Ubuntu, por ejemplo utilizando SSH, el usuario también se presenta con un intérprete de comandos. Los detalles sobre el acceso a un servidor remoto utilizando SSH se cubrirán en el capítulo titulado «Configuración de la autenticación basada en clave SSH en Ubuntu». Cuando se arranca un sistema basado en un servidor en el que no se ha instalado un entorno de escritorio, se entra en el shell inmediatamente después de que el usuario complete el procedimiento de inicio de sesión en la terminal de la consola física o en la sesión de inicio de sesión remota.
1.3 Introducción de comandos en el prompt
Los comandos se introducen en el prompt del shell simplemente escribiendo el comando y pulsando la tecla Enter. Mientras que algunos comandos realizan tareas en silencio, la mayoría mostrará algún tipo de salida antes de volver al prompt. Por ejemplo, el comando ls se puede utilizar para mostrar los archivos y directorios en el directorio de trabajo actual:
$ ls
Escritorio Documentos Descargas Música Imágenes Plantillas Públicas Vídeos
Los comandos disponibles están incorporados en el propio shell, o residen en el sistema de archivos físico. La ubicación en el sistema de archivos de un comando puede ser identificada usando el comando which. Por ejemplo, para saber dónde reside el ejecutable ls en el sistema de archivos:
$ which ls alias ls='ls --color=auto' /usr/bin/ls
Está claro que el comando ls reside en el directorio /usr/bin. Observe también que se ha configurado un alias, un tema que se tratará más adelante en este capítulo. El uso del comando which para localizar la ruta de acceso a los comandos que están incorporados en el shell dará como resultado un mensaje indicando que no se puede encontrar el ejecutable. Por ejemplo, al intentar encontrar la ubicación del comando history (que en realidad está incorporado en el shell en lugar de existir como un ejecutable en el sistema de archivos) resultará en una salida similar a la siguiente:
$ which history/usr/bin/which: no history in (/home/demo/.local/bin:/home/demo/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin)
1.4 Obtener información sobre un comando
Muchos de los comandos disponibles en el shell de Linux pueden parecer crípticos para empezar. Para encontrar información detallada sobre lo que hace un comando y cómo utilizarlo, utilice el comando man especificando el nombre del comando como argumento. Por ejemplo, para saber más sobre el comando pwd:
$ man pwd
Cuando se ejecuta el comando anterior, se muestra una descripción detallada del comando pwd. Muchos comandos también proporcionarán información adicional cuando se ejecutan con la opción de línea de comandos -help:
$ wc --help
1.5 Edición de la línea de comandos de Bash
Los primeros entornos de shell no proporcionaban ninguna forma de capacidad de edición de líneas. Esto significaba que si se detectaba un error al principio de una larga línea de comandos que se estaba escribiendo, había que borrar todos los caracteres siguientes, corregir el error y volver a introducir el resto del comando. Afortunadamente, Bash ofrece una amplia gama de opciones de edición de la línea de comandos, como se indica en la siguiente tabla:
Secuencia de teclas | Acción |
Ctrl-b o Flecha izquierda | Mover el cursor hacia atrás una posición |
Ctrl-f o Flecha derecha | Mover el cursor hacia delante una posición |
Borrar | Borrar el carácter que se encuentra debajo del cursor |
Retroceso | Borrar el carácter a la izquierda del cursor |
Ctrl-_ | Deshacer el cambio anterior (puede repetirse para deshacer todos los cambios anteriores) |
Ctrl-a | Mover el cursor al inicio de la línea |
Ctrl-e | Mover el cursor al final de la línea |
Meta-f o Esc y luego f | Mover el cursor hacia adelante una palabra |
Meta-b o Esc y luego b | Mover el cursor hacia atrás una palabra |
Ctrl-l | Borrar la pantalla de todo excepto del comando actual |
Ctrl-k | Borrar hasta el final de la línea desde la posición actual del cursor |
Meta-d o Esc entonces d | Borrar al final de la palabra actual |
Meta-DEL o Esc entonces DEL | Borrar al principio de la palabra actual |
Ctrl-w | Borrar desde la posición actual del cursor hasta el espacio en blanco anterior |
Tabla 9-1
1.6 Trabajar con el historial del shell
Además de las características de edición de la línea de comandos, el shell Bash también proporciona soporte para el historial de la línea de comandos. Se puede ver una lista de comandos ejecutados previamente utilizando el comando history:
$ history1ps2ls3ls –l /4ls5man pwd6man apropos
Además, se puede utilizar Ctrl-p (o flecha hacia arriba) y Ctrl-n (o flecha hacia abajo) para desplazarse hacia adelante y hacia atrás a través de los comandos introducidos previamente. Cuando aparezca el comando deseado del historial, pulse la tecla Enter para ejecutarlo.
Otra opción es introducir el carácter ‘!’ seguido de los primeros caracteres del comando que se va a repetir, seguido de la tecla Enter.
1.7 Abreviatura de nombre de archivo
Muchos comandos del shell toman uno o más nombres de archivo como argumentos. Por ejemplo, para mostrar el contenido de un archivo de texto llamado list.txt, el comando cat se utilizaría de la siguiente manera:
$ cat list.txt
De manera similar, el contenido de múltiples archivos de texto podría mostrarse especificando todos los nombres de archivo como argumentos:
$ cat list.txt list2.txt list3.txt list4.txt
En lugar de escribir cada nombre, se puede utilizar la coincidencia de patrones para especificar todos los archivos con nombres que coincidan con ciertos criterios. Por ejemplo, el carácter comodín «*» puede utilizarse para simplificar el ejemplo anterior:
$ cat *.txt
El comando anterior mostrará el contenido de todos los archivos que terminen con una extensión .txt. Esto podría restringirse a cualquier nombre de archivo que comience con list y termine en .txt:
$ cat list*.txt
Se pueden especificar coincidencias de un solo carácter utilizando el carácter ‘?’:
$ cat list?.txt
1.8 Completar el nombre de archivo y la ruta
En lugar de escribir un nombre de archivo o una ruta completos, o utilizar la coincidencia de patrones para reducir la cantidad de escritura, el shell proporciona la función de completar el nombre de archivo. Para utilizar la función de completar el nombre del archivo, simplemente introduzca los primeros caracteres del nombre del archivo o de la ruta y, a continuación, pulse la tecla Esc dos veces. El intérprete de comandos completará el nombre del archivo con el primer nombre de archivo o ruta del directorio que coincida con los caracteres introducidos. Para obtener una lista de posibles coincidencias, pulse Esc = después de introducir los primeros caracteres.
1.9 Redirección de entrada y salida
Como se ha mencionado anteriormente, muchos comandos del shell emiten información cuando se ejecutan. Por defecto esta salida va a un archivo de dispositivo llamado stdout que es esencialmente la ventana de terminal o consola en la que se ejecuta el shell. A la inversa, el shell toma la entrada de un archivo de dispositivo llamado stdin, que por defecto es el teclado.
La salida de un comando puede ser redirigida desde stdout a un archivo físico en el sistema de archivos usando el carácter ‘>’. Por ejemplo, para redirigir la salida de un comando ls a un archivo llamado files.txt, se requeriría el siguiente comando:
$ ls *.txt > files.txt
Al terminar, files.txt contendrá la lista de archivos en el directorio actual. Del mismo modo, el contenido de un archivo puede ser introducido en un comando en lugar de stdin. Por ejemplo, para redirigir el contenido de un archivo como entrada a un comando:
$ wc –l < files.txt
El comando anterior mostrará el número de líneas contenidas en el archivo files.txt.
Es importante tener en cuenta que el operador de redirección ‘>’ crea un nuevo archivo, o trunca un archivo existente cuando se utiliza. Para añadir a un archivo existente, utilice el operador ‘>>’:
$ ls *.dat >> files.txt
Además de la salida estándar, el shell también proporciona una salida de error estándar utilizando stderr. Mientras que la salida de un comando se dirige a stdout, cualquier mensaje de error generado por el comando se dirige a stderr. Esto significa que si stdout se dirige a un archivo, los mensajes de error seguirán apareciendo en el terminal. Este es generalmente el comportamiento deseado, aunque stderr también puede ser redirigido si se desea utilizando el operador ‘2>’:
$ ls dkjfnvkjdnf 2> errormsg
Al finalizar el comando, un error informando el hecho de que el archivo llamado dkjfnvkjdnf no pudo ser encontrado será contenido en el archivo errormsg.
Tanto stderr como stdout pueden ser redirigidos al mismo archivo usando el operador &>:
$ ls /etc dkjfnvkjdnf &> alloutput
Al finalizar la ejecución, el archivo alloutput contendrá tanto un listado del contenido del directorio /etc, como el mensaje de error asociado con el intento de listar un archivo inexistente.
1.10 Trabajando con Tuberías en el Shell Bash
Además de la redirección de E/S, el shell también permite que la salida de un comando sea canalizada directamente como entrada a otro comando. Una operación de tubería se logra colocando el carácter ‘|’ entre dos o más comandos en una línea de comandos. Por ejemplo, para contar el número de procesos que se ejecutan en un sistema, la salida del comando ps se puede canalizar a través del comando wc:
$ ps –ef | wc –l
No hay límite en el número de operaciones de canalización que se pueden realizar en una línea de comandos. Por ejemplo, para encontrar el número de líneas en un archivo que contienen el nombre Smith:
$ cat namesfile | grep Smith | wc –l
1.11 Configuración de alias
A medida que adquiera destreza con el entorno del shell es probable que se encuentre emitiendo frecuentemente comandos con los mismos argumentos. Por ejemplo, puede utilizar a menudo el comando ls con las opciones l y t:
$ ls –lt
Para reducir la cantidad de escritura que implica la emisión de un comando, es posible crear un alias que se asigna al comando y los argumentos. Por ejemplo, para crear un alias de forma que al introducir la letra l se ejecute el comando ls -lt, se utilizaría la siguiente sentencia:
$ alias l="ls –lt"
Al introducir l en el símbolo del sistema se ejecutará la sentencia original.
1.12 Variables de entorno
Las variables de entorno del shell proporcionan un almacenamiento temporal de datos y ajustes de configuración. El propio shell establece una serie de variables de entorno que pueden ser cambiadas por el usuario para modificar el comportamiento del shell. Se puede obtener una lista de las variables definidas actualmente utilizando el comando env:
$ envSSH_CONNECTION=192.168.0.19 61231 192.168.0.28 22MODULES_RUN_QUARANTINE=LD_LIBRARY_PATHLANG=en_US.UTF-8HISTCONTROL=ignoredupsHOSTNAME=demo-pc.ebookfrenzy.comXDG_SESSION_ID=15MODULES_CMD=/usr/share/Modules/libexec/modulecmd.tclUSER=demoENV=/usr/share/Modules/init/profile.shSELINUX_ROLE_REQUESTED=PWD=/home/demoHOME=/home/demoSSH_CLIENT=192.168.0.19 61231 22SELINUX_LEVEL_REQUESTED= ...
Tal vez la variable de entorno más útil es PATH. Ésta define los directorios en los que el shell buscará los comandos introducidos en el símbolo del sistema, y el orden en que lo hará. La variable de entorno PATH para una cuenta de usuario en un sistema Ubuntu recién instalado probablemente estará configurada como sigue:
$ echo $PATH/home/demo/.local/bin:/home/demo/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/ bin:/usr/local/sbin:/usr/sbin
Otra variable útil es HOME, que especifica el directorio de inicio del usuario actual. Si, por ejemplo, quieres que el shell busque también comandos en el directorio de scripts situado en tu directorio personal, modificarías la variable PATH de la siguiente manera:
$ export PATH=$PATH:$HOME/scripts
El valor actual de una variable de entorno existente puede mostrarse utilizando el comando echo:
$ echo $PATH
Puedes crear tus propias variables de entorno utilizando el comando export. Por ejemplo:
$ export DATAPATH=/data/files
Un truco útil para asignar la salida de un comando a una variable de entorno implica el uso de comillas (`) alrededor del comando. Por ejemplo, para asignar la fecha y la hora actuales a una variable de entorno llamada AHORA:
$ export NOW=`date`$ echo $NOWTue Apr 2 13:48:40 EDT 2020
Si hay configuraciones de variables de entorno o alias que necesita que se configuren cada vez que entra en el entorno del shell, pueden añadirse a un archivo en su directorio personal llamado .bashrc. Por ejemplo, el siguiente archivo .bashrc está configurado para establecer la variable de entorno DATAPATH y un alias:
# .bashrc # Source global definitionsif ; then . /etc/bashrcfi # User specific environmentPATH="$HOME/.local/bin:$HOME/bin:$PATH"export PATH # Uncomment the following line if you don't like systemctl's auto-paging feature:# export SYSTEMD_PAGER= # User specific aliases and functionsexport DATAPATH=/data/filesalias l="ls -lt"
1.13 Escribir scripts de shell
Hasta ahora nos hemos centrado exclusivamente en la naturaleza interactiva del shell Bash. Por interactivo nos referimos a introducir manualmente los comandos en el prompt uno a uno y ejecutarlos. De hecho, esto es sólo una pequeña parte de lo que el shell es capaz de hacer. Podría decirse que uno de los aspectos más poderosos del shell es la capacidad de crear scripts de shell. Los scripts de la shell son esencialmente archivos de texto que contienen secuencias de sentencias que pueden ser ejecutadas dentro del entorno de la shell para realizar tareas. Además de la capacidad de ejecutar comandos, el shell proporciona muchas de las construcciones de programación, como los bucles for y do y las sentencias if, que razonablemente podría esperar encontrar en un lenguaje de scripting.
Desgraciadamente, una visión detallada del shell scripting está más allá del alcance de este capítulo. Sin embargo, hay muchos libros y recursos web dedicados al shell scripting que hacen mucha más justicia al tema de lo que podríamos esperar aquí. En esta sección, por lo tanto, sólo proporcionaremos una pequeña muestra de shell scripting.
El primer paso en la creación de un shell script es crear un archivo (para los propósitos de este ejemplo lo llamaremos simple.sh) y añadir lo siguiente como primera línea:
#!/bin/sh
El #! se denomina «shebang» y es una secuencia especial de caracteres que indica que la ruta al intérprete necesario para ejecutar el script es el siguiente elemento de la línea (en este caso el ejecutable sh ubicado en /bin). Esto podría ser igualmente, por ejemplo, /bin/csh o /bin/ksh si cualquiera de los dos fuera el intérprete que se desea utilizar.
El siguiente paso es escribir un simple script:
#!/bin/shfor i in *do echo $idone
Todo lo que hace este script es iterar a través de todos los archivos en el directorio actual y mostrar el nombre de cada archivo. Esto puede ser ejecutado pasando el nombre del script como argumento a sh:
$ sh simple.sh
Para hacer que el archivo sea ejecutable (negando así la necesidad de pasarlo al comando sh) se puede utilizar el comando chmod:
$ chmod +x simple.sh
Una vez que el bit de ejecución se ha establecido en los permisos del archivo, se puede ejecutar directamente. Por ejemplo:
$ ./simple.sh
1.14 Resumen
En este capítulo de Ubuntu Essentials hemos hecho un breve recorrido por el entorno del shell Bash. En el mundo de los entornos gráficos de escritorio es fácil olvidar que la verdadera potencia y flexibilidad de un sistema operativo a menudo sólo se puede utilizar bajando por debajo de la interfaz de escritorio amigable y utilizando un entorno de shell. Además, la familiaridad con el shell es una necesidad cuando se requiere administrar y mantener sistemas basados en servidores que no tienen el escritorio instalado o cuando se intenta reparar un sistema que está dañado hasta el punto de que el escritorio o la interfaz Cockpit ya no se inician.