¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Programación shell en Unix/Linux
  3. Mecanismos esenciales del shell
Extrait - Programación shell en Unix/Linux ksh, bash, estándar POSIX (con ejercicios corregidos) (5ª edición)
Extractos del libro
Programación shell en Unix/Linux ksh, bash, estándar POSIX (con ejercicios corregidos) (5ª edición) Volver a la página de compra del libro

Mecanismos esenciales del shell

Presentación

Este capítulo presenta y explica de manera detallada las funcionalidades básicas del shell comúnmente utilizadas en los comandos Unix.

Comandos internos y externos

Un comando Unix pertenece a una de las dos siguientes categorías:

1. Comandos externos

Un comando externo es un archivo localizado en el árbol del sistema de archivos. Por ejemplo, cuando un usuario ejecuta el comando ls, el shell pide al núcleo de Unix cargar en memoria el archivo /usr/bin/ls.

Se consideran comandos externos los archivos que tengan uno de los formatos siguientes:

  • Archivos con formato binario ejecutable.

  • Archivos con formato de texto que representan un script de comandos (que puede estar escrito en shell o en otro lenguaje, como por ejemplo Python, Perl, etc.).

El comando file indica el tipo de datos contenidos en un archivo.

Ejemplos

El comando ls es un archivo con formato binario ejecutable. Resultado del comando file:

$ file / bin/ls 
/bin/ls: ELF 64-bit LSB executable x86-64, version 1,(SYSV), 
dynamically linked 7uses shared libs), for GNU/Linux 2.6.18, stripped 

El comando miscript.sh es un script en shell. Resultado del comando file:

$ file /home/cristina/miscript.shmiscript.sh: ascii text 

El comando miscript.pl es un script en Perl. Resultado del comando file:

$ file /home/cristina/miscript.pl 
miscript.pl: ascii text 

El argumento del comando file es un nombre de archivo expresado con ruta relativa o absoluta.

Los comandos externos se ejecutan por un shell hijo que actúa de intermediario (ver Figura 1).

images/02ri01.png

Figura 1: Ejecución de un comando externo

Los intérpretes shell...

Impresión por pantalla

1. El comando echo

bourne

posix

ksh

bash

El comando interno echo permite realizar impresiones por pantalla.

Ejemplo

$ echo ¡He aquí un libro de programación shell!  
¡He aquí un libro de programación shell! 
$ 

Ciertos caracteres tienen un significado especial cuando se ponen entre comillas (apóstrofes o comillas dobles). Estos caracteres son los caracteres de escape. El comando echo de bash deberá tener la opción -e para que se produzca la interpretación de estos caracteres.

Lista de caracteres de escape

Carácter de escape

Significado

\\

Antislash

\a

Warning

\b

Eliminar carácter anterior

\c

Eliminar salto de línea al final de la línea

\f

Salto de página

\n

Salto de línea

\r

Retorno de carro

\t

Tabulación horizontal

\v

Tabulación vertical

\0xxx

Valor de un carácter expresado en bytes

a. El carácter "\n"

Sirve para provocar un salto de línea.

Ejemplo con un shell Bourne o Korn

$ echo "He aquí un salto de línea\ny otro\ny el  
salto de línea natural del comando echo" 
He aquí un salto de línea 
y otro 
y el salto de línea natural del comando echo 
$ 

Las comillas son obligatorias:

$ echo a\nb  
anb 
$ echo "a\nb"  
a 
b 
$ 

Ejemplos con el shell bash

$ echo "a\nb"...

El carácter ~ (tilde)

ksh

bash

El carácter ~ representa el directorio de inicio del usuario actual.

Ejemplos

El usuario actual se llama cristina:

$ id 
uid=505(cristina) gid=505(ociensa) 

El directorio actual es /tmp:

$ pwd 
/tmp 

Copia del archivo /tmp/f1 en el directorio inicial (/home/cristina):

$ cp f1 ~ 

Copia del archivo /tmp/f1 en el directorio /home/cristina/docs:

$ cp f1 ~/docs 

Si el carácter ~ está inmediatamente seguido de una palabra, esta última se considera como un nombre de usuario.

Ejemplos

Copia del archivo f1 en el directorio de inicio del usuario sebastian (suponemos que se dispone de los permisos adecuados):

$ cp f1  ~sebastian 
$ ls  /home/sebastian/f1 
f1 
$ 

Copia del archivo f1 en el directorio /home/sebastian/dir:

$ cp f1  ~sebastian/dir 
$ ls  /home/sebastian/dir 
f1 

El comando interno cd

A continuación, presentamos las sintaxis particulares del comando cd.

posix

ksh

bash

Ejemplos

El comando cd sin argumento permite al usuario volver a su directorio de inicio:

$ cd 

Lo mismo que utilizando el carácter ~ :

$ cd ~ 

Cambiar al directorio de inicio del usuario sebastian:

$ pwd 
/home/cristina 
$ cd ~sebastian 
$ pwd 
/home/sebastian 

Volver al directorio anterior con cd -:

$ cd - 
/home/cristina 

Sustitución de nombres de archivos

Muchos comandos toman nombres de archivo como argumento. Estos últimos se pueden citar literalmente o se pueden especificar de forma más genérica. El shell propone un cierto número de caracteres especiales que permiten fabricar expresiones utilizadas como plantillas de nombres de archivo.

1. Expresiones básicas

bourne

posix

ksh

bash

a. El carácter *

Representa un conjunto de caracteres cualquiera (entre 0 y n caracteres).

Ejemplo

$ ls  
f12 f1.i FICa fic.c fic.s miscript.pl MISCRIPT.pl oso.c 

Mostrar todos los nombres de archivo que terminen por .c:

$ ls *.c  
fic.c oso.c 

Mostrar todos los nombres de archivo que comiencen por la letra f:

$ ls f*  
f12 f1.i fic.c fic.s 

b. El carácter ?

Representa un carácter cualquiera.

Ejemplos

Mostrar todos los nombres de archivo que tengan una extensión compuesta de un solo carácter:

$ ls *.?  
f1.i fic.c fic.s oso.c 

Mostrar todos los nombres de archivo compuestos de cuatro caracteres:

$ ls ???? 
f1.i FICa 

c. Los caracteres [ ]

Una pajera de corchetes representa la ubicación de un único carácter. Permite especificar la lista de caracteres que se esperan en una posición concreta en el nombre del archivo. También es posible usar las nociones de intervalo y negación.

Ejemplos de sintaxis de intervalos y de negación

Intervalos

[a-z]

una letra minúscula entre la a y la z

[a-zB-F]

una minúscula entre a y z O una mayúscula entre B y F

[A-Z0-9i]

una letra mayúscula O un número O la letra i

Negación

El ! se coloca solo en la primera posición dentro de los corchetes y expresa la negación de todos los caracteres citados entre corchetes.

[ !159a]

un carácter que no forma parte de la lista 1 5 9 a

[ !A-Z0-9i]

un carácter que no es ni una mayúscula, ni una cifra, ni la letra i

En bash y ksh, también es posible utilizar las clases de caracteres POSIX (para ver la lista completa, consulte el capítulo Anexos - Clases de caracteres POSIX). A continuación, se muestra un ejemplo.

Ejemplos

Archivos cuyo nombre empiece por f u o y termine por el carácter . seguido de minúscula:...

Separador de comandos

El carácter especial ; del shell permite escribir varios comandos en una misma línea. Los comandos se ejecutan secuencialmente.

Ejemplo

$ pwd  
/home/cristina 
$ ls  
$ mkdir dir ; cd dir ; pwd  
/home/cristina/dir 

Redirecciones

Las redirecciones se suelen usar en los comandos Unix. Permiten recuperar el resultado de uno o varios comandos en un archivo o provocar la lectura de un archivo por un comando. Esta sección explica detalladamente las diferentes sintaxis posibles con sus mecanismos internos asociados.

Las redirecciones son ejecutadas por el shell.

1. Entrada y salidas estándar de los procesos

Los procesos de Unix tienen, por defecto, su archivo terminal abierto tres veces, mediante tres descriptores de archivo diferentes. Un descriptor se puede ver como un canal de comunicación.

a. Entrada estándar

Al descriptor de archivo 0 se le llama también entrada estándar del proceso. Los procesos que esperan la entrada de información por parte del usuario desencadenan una lectura sobre el descriptor 0. Si este último está asociado al terminal, que es como está por defecto, se materializa al usuario como una petición de lectura del teclado.

La mayoría de los comandos utilizan la entrada estándar para desencadenar una lectura. Sin embargo, hay excepciones. Por ejemplo, el comando passwd abre el archivo terminal con otro descriptor.

b. Salida estándar

Al descriptor de archivo 1 se le llama también salida estándar del proceso. Por convenio, un proceso que desea enviar un mensaje de resultado al usuario tiene que hacerlo a través del descriptor 1. Si este último está asociado al terminal, que es como está por defecto, se materializa al usuario como una impresión por pantalla.

c. Salida de error estándar

Al descriptor de archivo 2 se le llama también salida de error estándar del proceso. Por convenio, un proceso que desea enviar un mensaje de error al usuario tiene que hacerlo a través del descriptor 2. Si este último está asociado al terminal, que es como está por defecto, se materializa al usuario como una impresión por pantalla.

2. Herencia

Los descriptores de archivos son heredados en la duplicación (fork()) y el remplazo (exec()). La figura 4 muestra que el shell ksh hijo, después del comando find, hereda la tabla de descriptores de archivos del shell padre. Este comando envía por la salida estándar los resultados y por la salida de error estándar la lista de directorios que el usuario no tiene permisos para explorar. Como las salidas...

Tuberías de comunicación

Una tubería (pipe en inglés) permite la comunicación entre dos procesos. La tubería se representa con una barra vertical (combinación de teclas [Alt Gr] 1 en un teclado QWERTY) situada entre dos comandos Unix. El resultado del comando de la izquierda va a parar a la tubería, mientras que el comando de la derecha leerá los datos que hay en ella para tratarlos.

Las figuras 12 y 13 representan el mecanismo interno asociado a la tubería de comunicación.

images/02ri12.png

Figura 9: Mecanismo interno de la tubería de comunicación - Primera etapa

images/02ri13.png

Figura 10: Mecanismo interno de la tubería de comunicación - Segunda etapa

Sean cuales sean los comandos presentes a cada lado de la tubería, el shell de trabajo detecta el carácter | en la línea de comandos y creará un shell hijo (1) que, a su vez, hace lo mismo (2). El primer shell hijo (PID=205) disocia su salida estándar del terminal y la conecta a la entrada de la tubería (3). El segundo shell hijo (PID=206) disocia su entrada estándar del terminal y la conecta a la salida de la tubería (4).

Cada shell hijo se remplaza con su comando (5a y 5b). Cada comando empieza entonces a ejecutarse. Cuando el comando who escribe en su salida estándar, los mensajes van dentro de la tubería (6a). Paralelamente, el comando mail lee su entrada estándar (6b), lo que provoca la extracción de los datos contenidos en la tubería.

Algunos aspectos importantes

  • La salida de error estándar del comando de la izquierda no va a la tubería.

  • Para que el uso de una tubería tenga sentido, es necesario que el comando situado a la izquierda de la tubería envíe datos a su salida estándar y que el comando situado a la derecha lea su entrada estándar.

1. Comandos que no leen su entrada estándar

Hay una serie de comandos que no interesa ponerlos detrás de una tubería, ya que no realizan lecturas en su entrada estándar. Este es el caso, por ejemplo, de los comandos siguientes: ls, who, find, chmod, cp, mv, rm, ln, mkdir, rmdir, date, kill, file, type, echo...

Para que estos comandos puedan explotar la salida de un pipe, es posible usar el comando xargs que servirá como intermediario (ver capítulo Los comandos filtro - El comando xargs).

2. Comandos...

Agrupación de comandos

La agrupación de comandos se puede usar para:

  • Redirigir la salida de varios comandos por pantalla hacia un mismo archivo o hacia una tubería.

  • Ejecutar múltiples comandos en el mismo entorno.

Ejemplo

Sólo la salida estándar del segundo comando se redirige al archivo resultado.

$ date ; ls > resultado  
sab ene 28 05:16:30 CET 2023 
$ cat resultado  
FIC 
archivo 
$ 

Los paréntesis ( ) y las llaves {} permiten agrupar los comandos. En el primer caso, los comandos se ejecutan en un shell hijo, en el segundo caso en el shell actual.

1. Paréntesis

En la mayoría de los casos, se emplean los paréntesis para agrupar los comandos.

Sintaxis

(cmdo1 ; cmdo2 ; cmdo3) 

Con los paréntesis, un shell hijo se crea sistemáticamente y es este el que trata la línea de comandos (con duplicaciones posteriores si es necesario).

Primer ejemplo

En este caso, el usuario se sirve de los paréntesis para redirigir la salida estándar de dos comandos:

$  (date ; ls) > resultado 
$ cat resultado 
sab ene 28 05:21:36 CET 2023 
FIC 
archivo 
$ 

Segundo ejemplo

El cambio de directorio se hace en un shell hijo.

Por lo tanto, los comandos pwd y ls tienen como directorio actual /tmp:

$ pwd 
/home/cristina 
$ (cd /tmp ; pwd ; ls) > listaarch 
$ cat listaarch 
/tmp                           (resultado...

Procesos en segundo plano

1. Principios

Los conceptos de segundo plano y primer plano son gestionados por el shell.

Por defecto, los comandos se ejecutan en primer plano. En este modo, el shell padre “duerme” a la espera del final del comando. Retomará el control únicamente cuando el comando haya terminado.

El carácter & es un carácter especial del shell que permite ejecutar el comando en segundo plano. El shell ejecuta el comando y rescribe inmediatamente su prompt a la espera de un nuevo comando. Como el shell y el comando se ejecutan en paralelo y ambos están vinculados al mismo terminal, es aconsejable redirigir las salidas del comando.

Ejemplo

El shell muestra el PID del comando (8247), así como su índice ([1]) en la lista de tareas en segundo plano ejecutadas a partir de este shell:

$ find / -size +2000 1>/tmp/resu 2 >/dev/null& 
[1] 8247 
$ 

Los procesos que se ejecutan en segundo plano no son sensibles a las acciones que provienen del teclado. Por lo tanto, es imposible detenerlos con [Ctrl] C, por ejemplo.

2. Control de tareas (trabajos)

jsh

(Bourne shell con Job Control)

posix

ksh

bash

El shell tiene comandos internos que permiten al usuario gestionar las tareas del terminal actual que están en primer o segundo plano. En la siguiente tabla se enumeran estas funcionalidades.

Comando

Acción

Teclas [Ctrl] Z

Solo para procesos en primer plano

Envío de la señal...

Ejercicios

1. Funcionalidades varias

a. Ejercicio 1: comandos internos y externos

¿Son los comandos umask y chmod comandos internos?

b. Ejercicio 2: generación de nombres de archivo

Sea la siguiente lista de archivos:

$ ls 
bd.class.php    header.inc.php  install.txt     readme.txt 
prueba           index.php       mail.class.php 

1.

Muestre los nombres de archivo que terminan en .php.

2.

Muestre los nombres de archivo que tengan la letra e en segunda posición.

3.

Muestre los nombres de archivo cuya primera letra esté comprendida entre a y e.

4.

Muestre los nombres de archivo que no comienzan por una vocal.

Expresiones extendidas (ksh, bash)

5.

Muestre los nombres de archivo que no terminan en .php.

6.

Muestre los nombres de archivo que no terminan ni con .txt ni con .php.

c. Ejercicio 3: separador de comandos

¿Cómo se escriben los dos comandos siguientes en la misma línea?

$ cd /tmp 
$ ls -l 

2. Redirecciones

a. Ejercicio 1

Liste todos los procesos del sistema y redirija el resultado a un archivo.

b. Ejercicio 2

Sea el comando who -A, que genera un mensaje de error:

$ who -A 
who : opción inválida -- 'A' 

1.

Relance este comando y redirija los errores a un archivo.

2.

Relance este comando y haga desaparecer los errores sin redirigir a un archivo en disco.

c. Ejercicio 3

Ejecute los comandos siguientes:

$ touch...