El shell y los comandos GNU
El shell bash
1. Función
Aunque las distribuciones de Linux destinadas al público en general permiten saltarse la introducción de instrucciones de texto al ofrecer entornos gráficos atractivos, un profesional de Linux no puede obviar el funcionamiento del intérprete de comandos y de los principales comandos asociados. Después de todo, los servidores Linux que disponen de una interfaz gráfica son poco frecuentes.
El intérprete de comandos, o simplemente intérprete, ejecuta las instrucciones introducidas con el teclado o en un script y le devuelve los resultados. Este intérprete es un programa comúnmente llamado shell. Se puede aproximar a la palabra kernel que vimos antes: el kernel significa núcleo. A menudo, está rodeado de una concha dura (piense en un hueso de albaricoque o melocotón, o los frutos secos, como las nueces o las avellanas). Como «shell» significa concha, viene a decir que es lo que rodea al núcleo de Linux: se utiliza mediante comandos. Por lo tanto, es una interfaz que funciona en modo texto entre el núcleo de Linux y los usuarios (avanzados) o las aplicaciones.
Hay varios shells: cada uno dispone de especificaciones propias. El Bourne Shell (sh) es el shell más conocido y habitual en los Unix. El C-Shell (csh) retoma la estructura del lenguaje C. El Korn Shell (ksh) es una evolución del Bourne Shell. El Z-Shell (zsh) es a su vez una evolución del Korn Shell. El shell de referencia en Linux se llama Bourne Again Shell (bash).
A continuación le presentamos una lista exhaustiva de intérpretes de comandos que puede encontrar en Linux:
-
sh: Thompson Shell (ya no existe);
-
sh: Bourne Shell (sustituyó al anterior);
-
bash: Bourne Again Shell;
-
ksh: Korn Shell;
-
csh: C Shell;
-
zsh: Z Shell;
-
tcsh: Tenex C Shell;
-
ash: A Shell;
-
dash: Debian Almquist Shell.
La lista de los shells disponibles en su instalación de Linux está en el archivo /etc/shells.
2. Bash: el shell por defecto
a. Un shell potente y libre
El bash es un derivado de Bourne Shell. Bourne es el nombre del principal programador de este shell. La expresión «Bourne Again» es un guiño a los orígenes del bash (Bourne) y un juego de palabras en «I born again», lo que significa «he nacido otra vez» o «reencarnado». El bash retoma...
La gestión de los archivos
1. El sistema de archivos
Un sistema de archivos, llamado comúnmente File System o FS, determina la organización de los datos en un soporte de almacenamiento, y por tanto, cómo gestiona y organiza el sistema operativo los archivos.
Linux es, como todo Unix, un sistema operativo completamente orientado a archivos. Se representa todo (o casi todo) con un archivo, tanto los datos (archivos de datos de cualquier tipo, como una imagen o un programa) como los periféricos (terminales, ratones, teclado, tarjeta sonido, etc.) o incluso los medios de comunicación (sockets, tuberías nombradas, etc.). Se puede decir que el sistema de archivos es el corazón de cualquier sistema Unix.
Ejemplo de árbol de directorio Linux
El sistema de archivos de Linux es jerárquico. Describe un árbol de directorios y subdirectorios, a partir de un elemento básico llamado raíz o root directory.
2. Los diferentes tipos de archivos
Distinguimos tres tipos de archivos: ordinarios, catálogo, especiales.
a. Los archivos ordinarios o regulares
Los archivos ordinarios se llaman también archivos regulares, ordinary files o regular files. Son archivos totalmente clásicos que contienen datos. Por datos se debe entender cualquier contenido:
-
texto;
-
imagen;
-
audio;
-
programa binario compilado;
-
script;
-
base de datos;
-
librería de programación;
-
etc.
Por defecto, nada permite diferenciar unos de otros, salvo la utilización de algunas opciones de determinados comandos (ls -F por ejemplo) o el comando file.
$ file /bin/bash
/bin/bash: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=
a6cb40078351e05121d46daa768e271846d5cc54, for GNU/Linux 3.2.0, stripped
Linux desconoce la noción de extensión de archivo como componente interno de la estructura del sistema de archivos. Dicho de otro modo, una extensión no es relevante dentro de un sistema de archivos y se la considera simplemente como parte del nombre. Sólo sirve para distinguir visual y rápidamente el posible contenido de un archivo en comparación con otro. No debemos hablar de extensión, sino de sufijo. Sin embargo, el primer término forma parte del lenguaje común, y se puede seguir utilizando: todo el mundo comprenderá...
Buscar archivos
1. Consideraciones generales
El comando find permite buscar archivos dentro de la estructura del sistema de archivos con la ayuda de criterios, y da la posibilidad de actuar sobre los resultados devueltos.
find ruta criterios opciones
Al ser recursivo el comando find, basta con indicar un directorio básico para que se desarrolle toda la estructura desde este directorio. La opción básica es -print (a menudo implícita en la mayoría de los Unix), que permite visualizar en pantalla los resultados.
[jolivares@client slyunix]$ find
.
./logos-cuadrado.tif
./logos-cuadrado.eps
./Pagina 5.pdf
./logos-cuadrado-grande.jpg
./LOGOS
./sitio_2.jpg
./pub_planeta.pdf
./index_logon_inc.php
./logo-iceberg.eps
./flyer
...
La visualización es relativa, ya que la ruta indicada es relativa. Si se hubiese especificado la ruta absoluta, la visualización habría sido absoluta.
2. Criterios de búsqueda
Los parámetros permiten definir los criterios de búsqueda. Si hay varios, puede combinarlos usando una Y lógica (criterio1 Y criterio2).
a. -name
-name permite una selección por nombres de archivos. Es posible utilizar los comodines ya vistos. Se coloca el criterio entre comillas. Aquí se visualiza la lista de todos los archivos desde la ubicación actual y que empiezan por "fic".
$ find . -name "fic*" -print
./fic1
./fic2
./fic3
./fic4
b. -type
-type permite una selección por tipo de archivos. Ya sabe que, además de los vínculos, los directorios y los archivos simples, están presentes otros tipos de archivos.
Código |
Tipo de archivo |
b |
Archivo especial en modo bloque |
c |
Archivo especial en modo carácter |
d |
Directorio (directory) |
f |
Archivo ordinario |
l |
Vínculo simbólico |
p |
Tubería con nombre (pipe) |
s |
Socket (Conexión de red) |
Se visualizan todos los directorios cuyo nombre empieza por "re".
$ find . -name "re*" -type d -print
./dir1
./dir2
c. -user y -group
-user y -group permiten una búsqueda sobre el propietario y el grupo de pertenencia de los archivos. Es posible precisar el nombre (usuario, grupo) o la ID (UID, GID). El ejemplo siguiente busca todos...
El editor vi
1. Presentación
El editor de Unix por defecto se llama vi (visual editor). No es de los más ergonómicos en comparación con editores en modo gráfico, pero tiene la ventaja de estar disponible y de utilizar la misma sintaxis básica en todos los Unix. Cada Unix propone en general una sintaxis extendida más allá de la sintaxis básica. El editor vi en Linux se llama vim. Vim respeta toda la sintaxis de vi, pero no es recíproco. Vi es pequeño: ocupa poco espacio en disco, consume poca memoria. Algunas de las opciones disponibles sólo funcionan en vim, como por ejemplo la gestión de buffers de memoria múltiples.
vi [opciones] Archivo [Archivo2 ...]
vi no tiene menús, tampoco interfaz gráfica, y no es intuitivo. Requiere conocerse de memoria un determinado número de atajos de teclado para poder utilizarlo. Si bien el aprendizaje es un poco difícil, una vez dominado, vi es rápido y práctico, hasta tal punto de ir más rápido que con editores de texto gráficos.
El debate que enfrenta a los partidarios de emacs (o de otros editores) con los de vi no tiene razón de ser. Cualquier sistema Linux (y Unix) dispone pase lo que pase del editor vi, por lo que es ineludible. Si tiene la oportunidad, puede instalar el editor que le guste, aunque esto, en entornos empresariales, de servidores, etc., no siempre será posible. Este debate genera muchos troles en los foros y tribunas.
2. Funcionamiento
Existen tres modos de funcionamiento:
-
Modo comando: las inserciones representan comandos. Se accede a ellos al pulsar [Esc]. Cada tecla o combinación de teclas activa una acción (supresión de líneas, inserciones, mover, copiar, pegar, etc.).
-
Modo inserción: se trata de la inserción de texto clásico.
-
Modo línea de comandos: una línea en la parte inferior de la pantalla permite insertar comandos especiales, validada con la tecla [Entrar]. Se accede a ella al presionar, en modo comando, la tecla «:».
Cuando ejecuta vi, por defecto está en modo comando. Para empezar a introducir texto hay que teclear un comando de añadido o inserción, lo que enerva rápidamente cuando uno no conoce vi: a o i. Por lo tanto, el comando [Esc] i permite insertar texto. Para salir, puede pasar...
Redirecciones
1. Fundamentos
Las redirecciones son una de las más importantes posibilidades proporcionadas por el shell. Por redirección se entiende la posibilidad de redireccionar la visualización de la pantalla hacia un archivo, una impresora o cualquier otro periférico, los mensajes de errores hacia otro archivo, de sustituir la introducción vía teclado por el contenido de un archivo.
Cualquier flujo de datos en entrada o salida de comando pasa por un canal. Como sucede con el agua, es posible desviar el curso de los datos hacia otro destino o desde otra fuente.
Linux utiliza canales de entradas/salidas para leer y escribir sus datos. Por defecto, el canal de entrada es el teclado, y el canal de salida, la pantalla. Los errores, direccionados por defecto a la pantalla, son tratados como un canal especial.
Es posible redireccionar estos canales hacia archivos o flujo de texto de manera transparente para los comandos Linux.
2. De salida
Se puede utilizar el carácter > para redireccionar la salida estándar (la que va normalmente en la pantalla). Luego se indica el nombre del archivo donde se colocarán los resultados de salida.
$ ls -l > resultado.txt
$ cat resultado.txt
total 1
-rw-r--r-- 1 Administ ssh_user 0 Jul 4 12:04 PEPITO
-rw-r--r-- 1 Administ ssh_user 0 Jul 25 15:13 resultado.txt
-rw-r--r-- 1 Administ ssh_user 171 Jul 25 15:13 test.txt
Si no existe, se creará el archivo. Si existe, se sobreescribirá su contenido, incluso si el comando tecleado no es correcto. El shell empieza primero por crear el archivo y luego ejecuta el comando.
Es un aspecto importante de las redirecciones: se interpretan las redirecciones de derecha a izquierda, y se instalan las redirecciones ANTES de la ejecución de los comandos. Hay que crear el archivo antes de poder escribir en él. De ahí que, incluso si el comando es falso...
Filtros y herramientas
Un filtro (o un comando filtro) es un programa que sabe escribir y leer datos por los canales estándares de entrada y salida. Modifica o trata si es preciso el contenido. wc es un filtro. Las herramientas no siempre se comportan como filtros. Permiten un determinado número de acciones en archivos y su contenido, como, por ejemplo, dar formato o imprimir.
1. Extracción de los nombres y rutas
El comando basename permite extraer el nombre del archivo en una ruta.
$ basename /tmp/seb/lista
lista
El comando dirname efectúa lo contrario, extrae la ruta.
$ dirname /tmp/seb/lista
/tmp/seb
2. Búsqueda de líneas
Se trata de extraer líneas de un archivo según varios criterios. Para ello, dispone de tres comandos: grep, egrep y fgrep, que leen los datos o bien desde un archivo de entrada, o bien desde el canal de entrada estándar.
a. grep
La sintaxis del comando grep es:
grep [Opciones] modelo [Archivo1...].
El modelo se compone de criterios de búsqueda que se parecen mucho a los criterios ya expuestos para vi, por ejemplo. No hay que olvidar que se debe interpretar estos criterios con el comando grep, y no con el shell. Por lo tanto, hace falta cerrar todos los caracteres.
$ cat fic4
Cerdo
Ternera
Buey
rata
Rata
buey
$ grep "^[bB]" fic4
Buey
buey
El comando grep también puede tomar algunas opciones interesantes.
-
-v efectúa la búsqueda inversa: se visualizan todas las líneas que no corresponden a los criterios.
-
-c sólo devuelve el número de líneas encontradas, sin mostrarlas.
-
-i no diferencia las mayúsculas de las minúsculas.
-
-n indica el número de línea para cada línea encontrada.
-
-l en el caso de archivos múltiples, indica en qué archivo se ha encontrado la línea.
$ grep -i "^b" fic4
Buey
buey
b. egrep
El comando egrep extiende los criterios de búsqueda y puede aceptar un archivo de criterios en entrada. Equivale a un grep -E. Emplea como criterios expresiones regulares.
egrep -f archivo_criterio archivo_búsqueda
Carácter especial |
Significado |
| |
O lógico, la expresión colocada antes o después debe desaparecer. |
(...) |
Agrupación de caracteres. |
[...] |
Un carácter tiene esta posición entre los indicados. |
. (punto)... |
Los procesos
1. Definición y entorno
Un proceso representa un programa en curso de ejecución y, al mismo tiempo, todo su entorno de ejecución (memoria, estado, identificación, propietario, padre...).
Los datos de identificación de un proceso son:
-
Un número de proceso único PID (Process ID): se numera cada proceso Unix con el fin de poder diferenciarlo de los otros. El primer proceso iniciado por el sistema es 1, y se trata de un proceso llamado generalmente init. Se utiliza el PID cuando se trabaja con un proceso. Iniciar 10 veces el mismo programa (mismo nombre) produce 10 PID diferentes.
-
Un número de proceso padre PID (Parent Process ID): cada proceso puede iniciar otros procesos, sus procesos hijos (child process). Cada proceso hijo debe contener, entre toda su información, el PID del proceso padre que lo inició. Todos los procesos tienen un PPID salvo el proceso 0, que es un seudoproceso que representa el inicio del sistema (crea el 1 init).
-
Un número de usuario y uno de grupo: corresponde al UID y al GID de la cuenta de usuario que inicia el proceso. El sistema lo utiliza para determinar, a través de la cuenta, los permisos que el proceso tiene para acceder a los recursos. Los procesos hijos heredan ambas cuentas. En algunos casos (que veremos más adelante) se puede modificar este comportamiento.
-
Duración y prioridad del proceso: la duración del proceso corresponde al tiempo de ejecución consumido desde la última invocación. En un entorno multitarea, el tiempo de procesador se comparte entre los procesos y no todos tienen la misma prioridad. Los procesos de más alta prioridad son ejecutados primero. Cuando un proceso está inactivo, su prioridad aumenta con el fin de tener la oportunidad de ser ejecutado. Cuando está activo, su prioridad baja con el fin de dejar paso a otro. El planificador de tareas del sistema es el que gestiona las prioridades y los tiempos de ejecución.
-
Directorio de trabajo activo: tras su inicio, se configura el directorio actual del proceso (desde el cual se inició). Este directorio servirá de base para las rutas relativas.
-
Archivos abiertos: tabla de los descriptores de archivos abiertos. Por defecto al principio sólo hay tres presentes: 0, 1 y 2 (los canales estándar). Con cada apertura de archivo o de nuevo canal, la tabla se rellena....
Más todavía del bash
1. Alias
Un alias es un atajo a un comando y a sus posibles parámetros. Se define con el comando alias. Utilizado sin argumentos, lista los alias disponibles.
$ alias
alias ..='cd ..'
alias ...='cd ../..'
alias cd..='cd ..'
alias dir='ls -l'
alias l='ls -alF'
alias la='ls -la'
alias ll='ls -l'
alias ls='ls $LS_OPTIONS'
alias ls-l='ls -l'
alias md='mkdir -p'
alias o='less'
alias rd='rmdir'
...
Puede crear sus propios alias.
$ alias deltree='rm -rf'
2. Agrupación de comandos
El encadenamiento de comandos es posible con «;». También es posible agrupar los comandos. Cuando ejecuta los comandos siguientes:
$ uname -a ; pwd ; ls -l >resultado.txt &
Se ejecuta únicamente el último comando en segundo plano y sólo se redirecciona su resultado en el archivo resultado.txt. Una solución sería:
$ uname -a >resultado.txt & ; pwd >>resultado.txt & ; ls -l >>resultado.txt &
[1] 18232
[2] 18238
[3] 18135
Es una solución compleja y no funcionará siempre. Además, incluso si se inician los comandos de manera secuencial, se ejecutan todos en paralelo. El primero que finalice será el primero...
Las variables
Se distinguen tres tipos: usuario, sistema y especiales. El principio consiste en poder asignar un contenido a un nombre de variable, en general una cadena de caracteres o valores numéricos.
1. Nomenclatura
Un nombre de variables obedece a ciertas reglas:
-
Se puede componer de letras minúsculas, mayúsculas, cifras, caracteres de subrayado.
-
El primer carácter no puede ser una cifra.
-
El tamaño de un nombre suele ser ilimitado (pero no hay que pasarse tampoco).
-
Las convenciones quieren que las variables de usuario estén en minúsculas para diferenciarlas de las variables de sistema. A elección del usuario.
2. Declaración y asignación
Se declara una variable en cuanto se le asigna un valor. Se efectúa la asignación con el signo =, sin espacio antes ni después del signo.
var=Hola
3. Acceso y visualización
Puede acceder al contenido de una variable colocando el signo $ delante del nombre de la variable. Cuando el shell encuentra el $, intenta interpretar la palabra siguiente como si fuera una variable. Si existe, entonces se sustituye el $nombre_variable por su contenido, o por un texto vacío en el caso contrario. Se habla también de referenciado de variable.
$ ruta=/tmp/seb
$ ls $ruta
...
$ cd $ruta
$ pwd
/tmp/seb
$ cd $ruta/dir1
$ pwd
/tmp/seb/dir1
Para visualizar la lista de las variables, se utiliza el comando env. Muestra las variables de usuario y de sistema, nombre y contenido.
$ env
HOSTTYPE=x86_64
SSH_CONNECTION=192.168.25.1 54564 192.168.25.26 22
LESSCLOSE=lessclose.sh %s %s
XKEYSYMDB=/usr/X11R6/lib/X11/XKeysymDB
LANG=es_ES.UTF-8
WINDOWMANAGER=/usr/bin/gnome
LESS=-M -I -R
JAVA_ROOT=/usr/lib64/jvm/jre-11-openjdk
HOSTNAME=localhost.localdomain
CONFIG_SITE=/usr/share/site/x86_64-unknown-linux-gnu
CSHEDIT=emacs
GPG_TTY=/dev/pts/2
AUDIODRIVER=pulseaudio
LESS_ADVANCED_PREPROCESSOR=no
COLORTERM=1
JAVA_HOME=/usr/lib64/jvm/jre-11-openjdk
ALSA_CONFIG_PATH=/etc/alsa-pulse.conf
MACHTYPE=x86_64-suse-linux
QEMU_AUDIO_DRV=pa
MINICOM=-c on
QT_SYSTEM_DIR=/usr/share/desktop-data
OSTYPE=linux
XDG_SESSION_ID=2
USER=alejandro
PAGER=less
MORE=-sl
PWD=/home/alejandro
...
_=/usr/bin/env...
Una variable puede contener caracteres...
Configuración del bash
1. Archivos de configuración
Se puede lanzar el shell bash en varios modos:
-
shell interactiva de conexión (login shell);
-
shell interactiva simple;
-
shell no interactiva;
-
el modo sh;
-
etc.
Según su modo de lanzamiento, el shell va a buscar y ejecutar varios scripts y archivos de configuración. Un archivo de configuración es un script de shell, una secuencia de comandos individuales que tiene como meta configurar el entorno del usuario.
a. Shell de conexión
Se lanza el shell de conexión después de la inserción del login y de la contraseña en la consola. Corresponde al que se ha precisado al final de cada línea de /etc/passwd. En este modo, el shell busca ejecutar, en este orden y si están presentes:
-
/etc/profile
-
~/.bash_profile
-
~/.bash_login
-
~/.profile
También encontramos /etc/profile/profile.d: el shell ejecutará el contenido de esta carpeta por medio del archivo /etc/profile, que en sus últimas directivas contiene un bucle que ejecutará todos los archivos aquí ubicados.
En el momento de desconectarse, intenta ejecutar:
-
- ~/.bash_logout
b. Shell simple
El shell interactivo simple corresponde a la ejecución del bash en una ventana (xterm, konsole), una consola o manualmente (teclear bash en una consola). En este caso, sólo se ejecutará el archivo siguiente si existe:
-
~/.bashrc
Observe que en muchas distribuciones...
Programación shell
1. Estructura y ejecución de un script
El shell no es solamente un simple intérprete de comandos, sino que dispone de un verdadero lenguaje de programación; en particular, con gestión de las variables, control del flujo y bucles, operaciones sobre las variables, funciones...
Se agrupan todas las instrucciones y los comandos dentro de un script. Durante su ejecución, cada línea se leerá una por una y se ejecutará. Una línea puede componerse de comandos internos o externos, de comentarios o estar vacía. Es posible diseñar varias instrucciones consecutivas siempre separadas por ; o relacionadas de manera condicional por && o ||. El ; es el equivalente de un salto de línea.
Por convención, los nombres de los scripts del shell terminan en general (no es obligatorio) por «sh» para el Bourne Shell y el Bourne Again Shell; «.ksh» para el Korn Shell y «csh» para el C Shell.
Para que un script sea ejecutable directamente:
$ chmod u+x miscript
Para ejecutarlo:
$ ./miscript
Para evitar el ./:
$ PATH=$PATH:.
$ miscript
Observe que se coloca el punto en última posición en el PATH. Ponerlo en primera posición puede representar un riesgo para la seguridad: se ha colocado un nuevo comando ls modificado en su directorio. Imagine los daños con un comando passwd.
Cuando se inicia un script, se crea un nuevo shell hijo que va a ejecutar cada uno de los comandos. Si es un comando interno, el nuevo shell lo ejecuta directamente. Si es un comando externo binario, se creará un nuevo hijo para ejecutarlo. En el caso de un script de shell, se inicia un nuevo shell hijo para leer este nuevo shell línea por línea.
Una línea de comentario siempre empieza con el carácter #. Se puede colocar un comentario al final de una línea que ya comporta comandos.
# La línea siguiente efectúa un ls
ls # La línea en cuestión
La primera línea reviste una importancia particular, ya que permite especificar qué shell va a ejecutar el script, esta línea se llama shebang:
#!/bin/bash
#!/bin/ksh
En el primer caso, es un script Bourne Again; en el otro, un script Korn. También puede pasar opciones a los comandos.
Si no sabemos dónde se encuentra el binario del intérprete de comandos...
Multiplexores de terminal
1. Presentación
Las herramientas screen tmux permiten gestionar varias ventanas de texto dentro de un mismo terminal. Los ejemplos se han hecho en screen, pero pueden aplicarse a tmux, ya que este último utiliza la misma sintaxis, sin embargo tmux es más configurable y atractivo. Mediante screen podrá en particular:
-
utilizar varias ventanas shell desde una unica conexión ssh,
-
mantener abiertas las conexiones ssh en caso de problemas de red (reinicio automático),
-
desconectarse y volver a conectar a sesiones shell y ssh de varios entornos (y redes),
-
ejecutar un comando shell de larga duración sin necesidad de mantener una sesión activa.
2. Utilización
a. Instalación y ayuda
Instale screen (apt-get install screen, o yum install screen) y ejecútelo.
$ screen
Aparte de la pantalla de información, con nada especial, obtenemos el símbolo de su shell. Si pulsamos sin embargo [Ctrl][a] + ? (o [Ctrl][a], soltamos y luego ?); obtenemos una pantalla de ayuda.
b. Ventanas
Salga de la ayuda y ejecute top. Ahora, abra una segunda ventana con [Ctrl][a] c. Un nuevo prompt de shell se muestra. Acabamos de abrir una nueva ventana de shell. Las ventanas se numeran de 0 a n. Para pasar de una ventana a la otra, utilice [Ctrl][a] 0, [Ctrl][a] 1, etc. Así [Ctrl][a] 0 retorna a la ventana donde top se ejecuta. Los comandos [Ctrl][a] n (next) y [Ctrl][a] p (previous) navegan...