Una introducción al Shell de Bourne para Unix

peron:~$ cat articulo.txt

Una Introducción al Shell Unix

Prólogo

He aquí el tratado introductorio la Shell de la Séptima Edición de UNIX, escrita por su propio autor, S. Bourne. Su clásico no sólo es interesante desde el punto de vista histórico, sino que por derecho propio guarda relevancia como buen tutorial general para el intérprete de comandos al que inspiró, el Bourne Again Shell del proyecto GNU.

La presente edición en forma de documento HTML es la "revisada para 4.3BSD por Mark Seiden", lo que permite datarlo al menos para finales de 1986. Sin embargo, ante diferencias lógicas de ambos sistemas - directorios de spool de correo, llamadas de sistema divergentes (BSD incorporó nuevas) - decidí proceder con arreglo al original. Debe tenerse en cuenta además ciertos detalles menores de la interfaz de terminal que el paso del tiempo hizo alterar (la tecla Retroceder o DEL usualmente ya no opera como caracter INTERRUPCIÓN, etc). Este inconveniente se replica en gran parte de la documentación original de UNIX producida en Murray Hill (incluso el excelente The Unix Programming Environment de Kernighan y Pike). La fuente parcial puede encontrarse Aquí

Retorno

Una Introducción al Shell UNIX

S. R. Bourne

Abstract

La shell es un lenguaje de programación de comandos que provee una interfaz de operación para el sistema operativo UNIX. Sus funcionalidades incluyen primitivas de control de flujo, pasaje de parámetros, sustitución de variables y cadenas. Es posible utilizar contrucciones de programación tales como while, if, then, else, case y for. Es posible establecer comunicación bidireccional entre la shell y los comandos. Pueden pasarse parámetros de valor de cadena a un comando, típicamente nombres de fichero o flags. En los comandos puede usarse un código de retorno para determinar un control de flujo, y la salida estándar de un comando puede utilizarse como entrada de la shell.

La shell puede modificar el ambiente el cual corre el comando. La entrada puede modificar el ambiente en el cual corren los comandos. La entrada y salida pueden redirigirse a ficheros, y pueden invocarse procesos comunicados a través de "tuberías". Los comandos se localizan buscando en directorios situados en el sistema de archivaje según una secuencia que puede redefinir el usuario. Los comandos pueden ser leídos desde el terminal o desde un fichero que permite utilizar guiones de shell capaces de ser almacenados para uso posterior.


UNIX es una marca registrada de AT&T Bell Laboratories en los EE.UU. y otros países.

1.0 Introducción

La shell es tanto un lenguaje de órdenes como un lenguaje de programación, que provee una interfaz para la operación del sistema operativo UNIX. Este memorando describe a la shell UNIX a través de ejemplos. La primer sección abarca la mayoría de los requerimientos diarios de los operadores de un terminal. Al estudiar dicha sección es ventajoso contar con cierta familiaridad con UNIX (consultar, por ejemplo, "UNIX para principiantes"). La Sección 2 describe aquellas características de la shell concebidas principalmente para su utilización en procedimientos de interpretación de comandos. Entre estas se incluyen las primitivas de control de flujo y las variables de valor de cadenas provistas por la shell. Será de gran ayuda contar con conocimientos de programación durante el estudio de esta sección. La última sección describe las características más avanzadas de la shell. Las referencias del tipo "ver tuberías (2)" describen a una sección del Manual de UNIX.

1.1 comandos Simples

Los comandos simples consisten en una o más palabras separadas por caracter de espacio en blanco. La primer palabra es el nombre del comando a ejecutar; cualquier palabra restante a continuación cobra el significado de argumento del comando. Por ejemplo,
	who
es un comando que presenta los nombres de aquellos usuarios con sesión en el sistema de cómputo UNIX. El comando
	ls -l
presenta un listado de los ficheros que se encuentran en el directorio actual. El argumento dado por la forma -l solicita a ls la presentación de información adicional de status, tamaño y la fecha de creación de cada fichero del listado.

1.2 comandos en Segundo Plano

Usualmente, para proceder a la ejecución de un comando, la shell genera un proceso de cómputo nuevo y aguarda por su finalización. El comando puede ejecutarse sin aguardar esta finalización. Por ejemplo,
	cc pgm.c &
ordena al compilador C proceder a la compilación del fichero pgm.c. El símbolo de & especificado al final consiste en un operador que instruye a la shell no aguardar la finalización de la orden. Para ayudar en la gestión de los proceso, la shell reporta el número de proceso correspondiente una vez que éste ha sido generado. Es posible obtener un listado de los procesos actualmente activos en el sistema utilizando el comando ps.

1.3 Redirecciionado de Entrada/Salida

La mayoría de los comandos producen como salida un resultado en la salida estándar, que - inicialmente - está conectada al terminal. Esta salida puede ser enviada a un fichero, por ejemplo, escribiendo:
	ls -l >fichero
La notación >fichero es interpretada por la shell, y no es interpretada como un argumento a ls. En caso que fichero no exista, la shell lo crearía; caso contrario los contenidos originales de fichero son reemplazados con la salida proveniente de ls. Es posible agregar la salida a una previamente existente en un fichero, utilizando la notación:
	ls -l >>fichero
En este caso, de no existir fichero también es creado.

Un comando puede recibir entrada estándar desde un fichero en lugar de hacerlo desde el terminal. Al escribir, por ejemplo:

	wc <fichero
El comando wc lee su entrada estándar - en este caso redirigida desde fichero - y presenta el número de cantidad de líneas, palabras y caracteres encontradas. Si sólo se requiere el número de líneas, entonces puede argumentarse tal solicitud
	wc -l <fichero

1.4 Tuberías y filtros

Es posible conectar la salida estándar de un comando a la entrada estándar de otro utilizando el operador de "caño", denotado por el símbolo |, tal como en la orden:
	ls -l | wc
Dos comandos así conectados constituyen una tubería y el efecto general es el mismo que ordenar
	ls -l >fichero; wc <fichero
excepto que no se intermedia fichero alguno. En lugar de utilizarlos, ambos procesos resultan conectados por un caño (ver pipe (2)), lo que amerita su ejecución en paralelo.

Las tuberías son unidireccionales y la sincronización se logra deteniendo a wc cuando no hay nada que leer, y deteniendo a ls cuando la tubería está llena.

Se conoce como filtro a un comando que lee su entrada estándar, la transforma de alguna manera, e presenta el resultado como salida. Uno de tales filtros, grep, escoge como su entrada aquellas líneas que contienen alguna cadena determinada. Por ejemplo,

	ls | grep reporte
presenta aquellas líneas de la salida de ls que contienen la cadena reporte (de existir). Otro filtro útil es sort. Por ejemplo,
	who | sort
presenta un listado de los usuarios con sesión en la máquina ordenado alfabéticamente.

Es posible que una cañería conste de más de dos comandos, por ejemplo:

	ls | grep reporte | wc -l
presenta el número del conteo de nombres de fichero del directorio actual los cuales contienen la cadena reporte.

1.5 Generación de nombre de fichero

Muchos comandos aceptan nombres de fichero como su argumento. Por ejemplo:
	ls -l main.c
presenta información relacionada al fichero main.c.

La shell provee un mecanismo para generar una lista de nombres de ficheros que coincidan con un determinado patrón. Por ejemplo,

	ls -l *.c
como argumentos de ls, genera una lista de todos aquellos nombres de ficheros en el directorio actual que finalicen en .c. El caracter * es un patrón de coincidencia que analiza coincidencias con cualquier cadena, incluyendo la cadena nula. En general los patrones se especifican de la siguiente manera:
*
Hace coincidir cualquier cadena de caracteres, incluyendo la cadena nula.
?
hace coincidir cualquier caracter simple.
[...]
Hace coincidir cualquiera de los caracteres encorchetados. De indicarse un par de caracteres separados por un signo "-", coincidirán cualquier caracter que léxica/alfabéticamente se encuentre entre dicho par.
Por ejemplo,
	[a-z]*
coincide todos los nombres en el directorio actual que comiencen con las letras desde la a a la z. En tanto que,
	/usr/pedro/test/?
coincide todos los nombres en el directorio /usr/pedro/test que consistan en un único caracter. De no hallarse un nombre de fichero coincidente con el patrón dado, entonces el patrón es pasado, sin alteración alguna, en forma de argumento.

Este mecanismo resulta útil tanto para ahorrar tiempo de mecanografiado, como para seleccionar nombres según un patrón determinado. También puede ser útil para encontrar ficheros. Por ejemplo:

	echo /usr/pedro/*/core
busca y presenta los nombres de todos los ficheros core en subdirectorios de /usr/pedro. (echo es un comando estándar de UNIX que presenta sus argumentos separados por espacios). Esta última funcionalidad puede resultar costosa, ya que requiere analizar todos los subdirectorios de /usr/pedro.

Existe una excepción para las reglas generales de patrones. El caracter `.' al comienzo de un nombre de fichero debe coincidir explícitamente. Por lo tanto:

	echo *
dará como eco todos los nombres de fichero en el directorio actual que no comiencen con `.', en tanto que:
	echo .*
dará como eco de todos los nombres de ficheros que comiencen con `.'. Esto evita hacer coincidir (sin advertirlos) los nombres `.' y `..', que significan "el directorio actual" y "el directorio padre" respectivamente. (Tenga presente que ls suprime presentar información de los ficheros `.' y `..'.)

1.6 Citado

Se conoce como meta-caracteres aquellos caracteres que tienen un significancia especial para la shell, tales como < > * ? | &,. El Apéndice B ofrece una lista completa de metacaracteres. Cualquier caracter precedido por una barra invertida \ resulta citado, perdiendo dicho significado especial (si lo tuviese). La \ resulta anulado de modo que
	echo \?
producirá el eco de un ? único, y
	echo \\
dará eco de una única \. Se ignorará la secuencia \newline para permitir que cadenas largas continúen a través de más de una línea.

El meta-caracter \ resulta conveniente para citar caracteres únicos. Al existir más de un caracter que requiera ser citado, el mecanismo anteriormete decripto puede considerarse torpe y propenso al error. Esa posible entonces citar una cadena de caracteres cerrándolas entre apóstrofos, lo que es más simple. Por ejemplo:

	echo xx'****'xx
dará como eco
	xx****xx
No es necesario que la cadena citada contenga una cita simple, sino que incluso si contiene caracteres de nueva línea, estos serían preservados. Este mecanismo de citado resulta el más simple, y se lo recomienda para el uso casual.

También está disponible Un tercer mecanismo de citado que emplea comillas. Este impide el interpretado de algunos - pero no todos - los meta-caracteres. La discusión de los detalles se retrasará hasta la sección 3.4.

1.7 Prompt

Cuando la shell se usa desde un terminal, presenta una venia antes de leer un comando. Por defecto esta venia consiste en un caracter `$ '. Puede ser cambiada diciendo, por ejemplo:
	PS1=siquerido
que configura la venia para que sea la cadena siquerido. Si se mecanografía una nueva línea y se necesita mayor entrada, entonces la shell dará la venia `> '. A veces esto puede ser provocado por mecanografiar un signo de apóstrofe. Si es inesperado, entonces introducir una interrupción (DEL) retornará al shell para leer otro comando. Esta venia puede cambniarse, diciendo, por ejemplo:
	PS2=mas

1.8 La shell y login

Siguiendo al login (1) se llama a la shell para leer y ejecutar comandos mecanografiados en el terminal. Si el directorio de login del usuario contiene el fichero .profile entonces asume que este contiene comandos, y resulta intepretado por la shell antes de leer algún comando del terminal.

1.9 Resúmen

ls
presenta los nombres de los ficheros en el directorio actual.
ls >fichero
Coloca la salida de ls en fichero.
ls | wc -l
presenta el númereo de ficheros en el directorio actual.
ls | grep viejo
presenta aquellos nombres de fichero que contiene la cadena viejo.
ls | grep reporte | wc -l
presenta el número de ficheros cuyo nombre contiene la candena reporte.
cc pgm.c &
Ejecuta cc en segundo plano.

2.0 Guiones de shell

La shell puede ser utilizada para leer y ejecutar mandatos contenidos en un fichero. Por ejemplo:
	sh fichero [ argumentos ... ]
ordena a la shell leer los comandos contenidas en fichero. Tal fichero se conoce como un procedimiento de comandos o un guion de shell. (N.d.T: Se ha preferido el uso actual de guión o shell script, de la traducción oficial de "procedimiento de comandos"). Junto con su llamada a ejecución pueden proporcionarse argumentos, y ser referidos a fichero usando los parámetros posicionales $1, $2, .... Por ejemplo: si el fichero wg contiene:
	who | grep $1
entonces
	sh wg pedro
es equivalente a
	who | grep pedro
Los fiheros de UNIX tienen tres atributos independientes, read, write y execute. El comando de UNIX chmod (1) puede ser usado para ordenar que un fichero sea ejecutable. Por ejemplo:
	chmod +x wg
asegura que el fichero wg cuente con atributo de ejecución. Tras ello, la orden
	wg pedro
es equivalente al mandato
	sh wg pedro
Esto permite utilizar los guiones de shell y los programas binarios de forma intercambiable. En cualquiera de los casos, se crea un nuevo proceso para ejecutar la orden.

Así como es posible proveer nombres para los parámetros posicionales, es posible utiliazr número de parámetros posicional durante la llamada, siguiendo una sintaxis $#. El nombre del fichero en ejecución es disponible como $0.

Se puede usar un parámetro de shell especial $* para sustituir todos los parámetros posicionales exceptuando $0. Un uso típico de esto es proveer algunos argumentos por defecto, como en

	nroff -T450 -ms $*
que simplemente añade algunos argumentos a aquellos ya dados.

2.1 Flujo de Control - for

Uno de los usos frecuentes de los guiones de shell es conformar un bucle a través de los argumentos ($1, $2, ...), ordenando la ejecución de órdenes una vez para cada argumento. Un ejemplo de un guión tal es tel, que busca el fichero /usr/lib/telnos que contienen líneas de la forma
	...
	pedro mh0123
	beto mh0789
	...
El texto de tel es
	for i
	do grep $i /usr/lib/telnos; done
El comando
	tel pedro
presenta aquellas líneas en /usr/lib/telnos que contienen la cadena pedro.
	tel pedro beto
presenta aquellas líneas que contienen pedro seguidas por aquellas para beto.

La notación de bucle for es reconocida por la shell y tiene la sintaxis general

	for nombre in w1 w2 ...
	do lista-de-comandos
	done
Una lista-de-comandos es una secuencia de uno o más comandos simples separados o terminados por una línea nueva o un punto y coma. Además, las palabras reservadas como do y done sólo son reconocidas si son seguidas por una nueva línea o un punto y coma. nombre es una variable de shell que está establecida a las palabras w1 w2 ... en turno a cada vez que lista-de-comandos siguiendo a do es ejecutada. Si in w1 w2 ... está omitido, entonces el bucle se ejecuta una vez para cada parámetro posicional; esto es, se asume in $*.

Otro ejemplo del uso del bucle for es el comando create cuyo texto es:

	for i do >$i; done
El comando
	create alfa beta
asegura que dos ficheros vacíos alfa y beta existan y estén vacíos. La notación >fichero puede usarse por sí misma para crear o eliminar el contenido de un fichero. Tenga también presente que se requiere un punto y coma ";" (o un caracter de nueva línea) antes del done.

2.2 Flujo de control - case

Se provee un arbolado de rumbo mútiple para la notación case. Por ejemplo,
	case $# in
	     1) cat >>$1 ;;
	     2) cat >>$2 <$1 ;;
	     *) echo \'uso: append [ desde ] hasta\' ;;
	esac
es un comando de agregado. Cuando se lo llama con un argumento como en
	append fichero
$# es la cadena 1 y la entrada estándar es copiada al final de fichero usando el comando cat.
	append fichero1 fichero2
agrega los contenidos de fichero1 al fichero2. Si el número de los argumentos dados a append es diferente a 1 o 2, entonces se presenta un mensaje indicando la forma debida de uso.

La sintaxis general del comando case es

	case palabra in
	     patrón) lista-de-comandos;;
	     ...
	esac
La shell intenta coincidir palabra con cada patrón en el orden en el cual aparecen los patrones. Si se encuentra una coincidencia, la lista-de-comandos asociada es ejecutada, y la órden del case se completa. Ya que * es el patrón que coincide con cualquier cadena, puede utilizarse para definir el case por defecto.

Debe advertir que no se realizan revisiones que aseguren que sólo un patrón coincide con el argumento del case. La primera coincidencia encontrada define el conjunto de comandos a ejecutar. En el ejemplo que se ofrece a continuación, las órdenes que siguen al segundo * no resultarán ejecutadas jamás.

	case $# in
	     *) ... ;;
	     *) ... ;;
	esac
Otro ejemplo del uso de la construcción case consiste en la distinción entre las formas de argumentos posibles. El siguiente ejemplo es un fragmento de un comando cc.
	for i
	do case $i in
	   -[ocs])      ... ;;
	   -*)  echo \'flag desconocido $i\' ;;
	   *.c) /lib/c0 $i ... ;;
	   *)   echo \'argumento inesperado $i\' ;;
	   esac
	done
A fin de permitir que el mismo comando resulte asociado con más de un patrón, el comando case provee el uso de patrones alternativos, separados por un |. Por ejemplo,
	case $i in
	     -x|-y)     ...
	esac
es equivalente a
	case $i in
	     -[xy])     ...
	esac
Las convenciones de citado usuales aplican, de modo que
	case $i in
	     \?)       ...
coincidirá el caracter ?.

2.3 Documentos Here

El guión de shell tel de la sección 2.1 usa el fichero /usr/lib/telnos para suplir los datos a grep. Una alternativa a esto es incluir tales datos dentro del guión de shell, en forma de un documento here, tal como
	for i
	do grep $i <<!
	   ...
	   pedro mh0123
	   beto mh0789
	   ...
	!
	done
En tal ejemplo, la shell interpreta las líneas entre <<! y ! como la entrada estándar para grep. La cadena ! es abitraria, el documento resulta terminado por una línea consistente en la cadena que sigue a <<.

Los parámetros resultan sustituidos en el documento antes de hacerlos disponibles a grep, según se ilustra en el siguiente guión de shell denominado edg.

	ed $3 <<%
	g/$1/s//$2/g
	w
	%
La llamada
	edg cadena1 cadena2 fichero
por tanto equivalente al comando
	ed fichero <<%
	g/cadena1/s//cadena2/g
	w
	%
cuyo mandato es sustituir todas las iteraciones de cadena1 en fichero por cadena2. La sustitución puede prevenirse usando "\" para citar el caracter especial $, como si se hiciese
	ed $3 <<+
	1,\$s/$1/$2/g
	w
	+
(Esta versión de edg es equivalente a la primera, excepto que ed presentará un ? si no existen ocurrencia de la cadena $1.) La sustutución dentro de un documento here puede ser impedida enteramente citando la cadena de terminación, por ejemplo,
	grep $i <<\#
	...
	#
El documento es presentado sin modificación a grep. Esta última forma es más eficiente, si la sustitución de parámetros no es requerida en un documento here.

2.4 Variables de Shell

La shell provee variables cuyo valor está formado por cadenas. Los nombres de las variables comienzan con una letra y consisten de letras, dígitos y guiones bajos "_". Las variables pueden ser valores dados por escrito, por ejemplo
	user=pedro box=m000 acct=mh0000
que asigna valores a las variables user, box y acct. Una variable puede establecerse a una cadena nula diciendo, por ejemplo,
	null=
El valor de la variable se substituye precediendo su nombre con $; por ejemplo:
	echo $user
dará como eco pedro.

Las variables pueden usarse forma interactiva para proveer abreviaturas a las cadenas comunmente usadas. Por ejemplo:

	b=/usr/pedro/bin
	mv pgm $b
moverá el fichero pgm desde el directorio actual al directorio /usr/pedro/bin. Una notación más general está disponible para la sustitución de parámetro (o variable), como en
	echo ${user}
la cual es equivalente a
	echo $user
y es usada cuando el nombre de parámetro es seguido por una letra o dígito. Por ejemplo,
	tmp=/tmp/ps
	ps a >${tmp}a
direcciona la salida de ps al fichero /tmp/psa, mientras que,
	ps a >$tmpa
ordena que el valor de la variable tmpa sea sustituído.

Excepto para $? lo siguiente es configurado inicialmente por la shell. $? es establecido luego de ejecutar cada comando.

$?
el estatus de salida (código de retorno) del último comando ejecutado como una cadena decimal. La mayoría de los comandos devuelve un estatus de salida cero si se completan exitosamente, de otro modo retornan un status de salida no cero. Se considerarán luego los códigos de valor de retorno bajo los comandos if y while.
$#
El número de los parámetros posicionales (en decimal). Usado, por ejemplo, en el comando append para revisar el número de parámetros.
$$
El número de procesos de esta shell (en decimal). Como los números de procesos son únicos entre todos los procesos existentes, esta cadena es frecuentemente usada para generar nombres de fichero temporales únicos. Por ejemplo:
	ps a >/tmp/ps$$
	...
	rm /tmp/ps$$
$!
El número de proceso del último proceso ejecutado en segundo plano (en decimal).
$-
Las flags del shell actual, tales como -x y -v.
Algunas variables cuentan con un significado especial para la shel, y deberían ser evitadas para su uso general.
$MAIL
Cuando es usada interactivamente, la shell busca en el fichero especificado por esta variable antes de proporcionar una Venia. Si el fichero especificado ha sido modificado desde la última vez que fue revisado, la shell presenta el mensaje you have mail antes de hacer la venia aguardadno la siguiente órden. Esta variable típicamente se configura en el fichero .profile situado en el directorio de login del usuario. Por ejemplo:
	MAIL=/usr/mail/pedro
$HOME
Argumento por defecto para el comando cd. El directorio actual se utiliza para resolver las referencias de nombre de fichero que no comienzan con un /, y se cambia empleando el comando cd. Por ejemplo,
	cd /usr/pedro/bin
hace que el directorio actual sea /usr/pedro/bin.
	cat wn
presentará en el terminal el fichero wn situado en este directorio actual. El comando cd sin argumentos es equivalente a
	cd $HOME
Esta variable es establecida también en el perfil de login del usuario.
$PATH
Listado de directorios que contienen comandos (la ruta de búsqueda). Toda vez que se ordena la ejecución de un comando a la shell, esta busca un fichero ejecutable respectivo en el listado rutas de búsqueda. Si $PATH no está establecida, entonces por defecto se lo busca en el directorio actual, /bin, y /usr/bin. En caso contrario, $PATH consiste de nombres de directorio separados por un :. Por ejemplo,
	PATH=:/usr/pedro/bin:/bin:/usr/bin
especifica al directorio actual (la cadena nula antes del primer :), /usr/pedro/bin, /bin y /usr/bin deberán ser analizados durante la búsqueda, en ese órden. De esta manera, se posibilita a los usuarios individuales distener sus propios comandos "privados" a los que acceden, independientemente del directorio actual. Si el nombre de comando contiene un / entonces esta búsqueda de directoria se ignora; solo se realiza un intento de ejecutar el comando.
$PS1
La primera cadena de venia de shell, por defecto, `$ '.
$PS2
La venia de shell cuando se necesita una entrada adicional, por defecto, `> '.
$IFS
El conjunto de caracteres utilizados por blank interpretation (ver sección 3.4).

2.5 El comando test

El comando test, aunque no es parte de la shell, está pensado para ser usado por programas de la shell. Por ejemplo,
	test -f fichero
devuelve un status de salida cero si fichero existe, o un status de salida no cero si se produce lo contrario. En general test evalúa un predicado y devuelve el resultado en forma de su status de salida. Aquí se proporcionan ciertos argumentos frecuentemente utilizados de test; consulte test (1) por una especificación completa.
test s
true si el argumento s no es una cadena nula
test -f fichero
true si fichero existe
test -r fichero
true si fichero tiene permiso de lectura
test -w fichero
true si fichero tiene permiso de escritura
test -d fichero
true si fichero es un directorio

2.6 Control de Flujo - while

Las acciones del bucle for y el arbolado case son determinadas según los datos disponibles a la shell. Un bucle while o until y un arbolado if then else también son provistos a aquellas acciones determinadas por el estatus de salida devuelto por los comandos. Un bucle while tiene la sintaxis general
	while lista-de-comandos1
	do lista-de-comandos2
	done
El valor probado por el comando while es el estado de salida del último comando simple que sigue al while. Cada iteración del bucle lista-de-comandos1 se ejecuta; si se retorna un valor de estado de salida cero, entonces se ejecuta lista-de-comandos2; de lo contrario, el bucle finaliza. Por ejemplo,
	while test $1
	do ...
	   shift
	done
es equivalente a
	for i
	do ...
	done
shift es un comando de la shell que renombra los parámetros posicionales $2, $3, ... como $1, $2, ... y descarta $1.

Otro tipo de uso para el bucle while/until es aguardar hasta que ocurra algún evento externo y luego correr algunos comandos. En un bucle until la condición de terminación es invertida. Por ejemplo,

	until test -f fichero
	do sleep 300; done
	commands
hará bucle hasta que exista fichero. Cada iteración del bucle aguardará durante cinco minutos antes de intentar nuevamente. (Presumiblemente, otro proceso crea eventualmente el fichero)

2.7 Flujo de Control - if

También está disponible un arbolado condicional general de la sintaxis
	if lista-de-comandos
	then    lista-de-comandos
	else    lista-de-comandos
	fi
que evalúa el valor devuelto por el último comando simple que sigue al if.

El comando if puede usarse en conjunción con el comando test para evaluar la existencia de un fichero como en

	if test -f fichero
	then    process fichero
	else    do something else
	fi
Un ejemplo del uso de las construcciones if, case y for se da en la sección 2.10.

Un comando de evaluación múltiple ifde la sintaxis

	if ...
	then    ...
	else    if ...
		then    ...
		else    if ...
			...
			fi
		fi
	fi
puede escribirse usando la extensión de la notación if,
	if ...
	then    ...
	elif    ...
	then    ...
	elif    ...
	...
	fi
El siguiente ejemplo es el comando touch que cambia el horario de "última modificación" para una lista de ficheros. El comando puede ser usado en conjunto con make (1) para forzar una recompilación de una lista de ficheros.
	flag=
	for i
	do case $i in
	   -c)  flag=N ;;
	   *)   if test -f $i
		then    ln $i basura$$; rm basura$$
		elif test $flag
		then    echo fichero \'$i\' no existe
		else    >$i
		fi
	    esac
	done
El flag -c se utiliza en este comando para forzar la creación de ficheros subsecuentes si no existiesen. En caso contrario - si el fichero no existe - se presenta un mensaje de error. La variable de shell flag resulta establecida a alguna cadena no nula si se encuentra el argumento -c. Los comandos
	ln ...; rm ...
crean un enlace al fichero y luego lo remueve, provocando por lo tanto la actualización del horario de última modifición.

La secuencia

	if comando1
	then    comando2
	fi
puede ser escrita
	comando1 && comando2
Conversamente,
	comando1 || comando2
ejecuta comando2 sólo si comando1 falla. En cada caso, el valor devuelto es aquél del último comando simple ejecutado.

2.8 Agrupamiento de comandos

Los comandos pueden ser agrupados de dos maneras,
	{ lista-de-comandos ; }
y
	( lista-de-comandos )
En la primera, simplemente es ejecutada lista-de-comandos. La segunda manera ejecuta lista-de-comandos en forma de proceso separado. Por ejemplo,
	(cd x; rm basura )
ejecuta rm basura en el directorio x sin proceder a cambiar el directorio actual del shell que lo ha invocado.

Los comandos

	cd x; rm basura
surten el mismo efecto, pero dejan al shell invocante en el directorio x.

2.9 Depurando guiones de shell

La shell provee dos mecanismos de trazabilidad para asistir al depurado de los guiones de shell. El primero de ellos se invoca dentro del guion, como en:
	set -v
v ofrece un resultado de respuesta verbosa y provoca la devolución de las líneas del guión de shell, impresas en la medida que son leídas. Esto resulta útil para asistir en la tarea de aislar errores sintácticos. Puede ser invocárselo sin alterar el guión de shell, indicando
	sh -v script ...
en donde script es el nombre del guión de shell. Esta flag puede ser utilizada en conjunto con el flag -n que impide la ejecución de comandos subsecuentes (tenga presente que indicar set -n en el terminal, hará que este deje de responder hasta que produzca el ingreso de un caracter EOF (N.d.T.: EOF: Fin de Fichero, se logra con Ctrl+d).

El comando

	set -x
producirá una traza de ejecución. Tras la acción de sustitución de parámetros, se presenta cada órden en la medida que resulta ejecutada (es posible evaluarlas en el terminal para considerar su resultado). Ambos flags pueden desactivarse indicando
	set -
y la configuración actual de las flags de la shell están disponibles como $-.

2.10 El comando man

El siguiente es el comando man que se usa para presentar secciones del manual de UNIX. Se lo llama, por ejemplo, con
	$ man sh
	$ man -t ed
	$ man 2 fork
se presentará la primer sección del manual para sh. Como no se especificó sección, se usa la sección 1. El segundo ejemplo usará la opción de código de fotocomposición (ordenada con la opción -t) para ed. La última orden se encarga de presentar la página de manual de fork a partir de la sección 2.
	cd /usr/man

	: 'colon is the comment command'
	: 'default is nroff ($N), section 1 ($s)'
	N=n s=1

	for i
	do case $i in
	   [1-9]*)      s=$i ;;
	   -t)  N=t ;;
	   -n)  N=n ;;
	   -*)  echo unknown flag \'$i\' ;;
	   *)   if test -f man$s/$i.$s
		then    ${N}roff man0/${N}aa man$s/$i.$s
		else    : 'look through all manual sections'
			found=no
			for j in 1 2 3 4 5 6 7 8 9
			do if test -f man$j/$i.$j
			   then man $j $i
				found=yes
			   fi
			done
			case $found in
			     no) echo \'$i: manual page not found\'
			esac
		fi
	   esac
	done
Figura 1. Una versión del comando man

3.0 Parámetros de palabra clave

Las variables de shell pueden ser valores dados por asignación o por invocación durante el tiempo de ejecución de un guión de shell. Un argumento de guión de shell con la forma nombre=valor que procede al nombre de comando, provoca que valor resulte asignado a nombre antes que comience la ejecución del guión. El valor de nombre en la shell invocante no resulta afectado. Por ejemplo,
	usuario=pedro comando
ejecutará comando con el usuario configurado para ser pedro. El flag -k provoca que los argumentos de la forma nombre=valor resulten interpretados en esta forma en cualquier lugar de la lísta de argumentos. Tales nombres a veces son conocidos como parámetros de palabras clave. De restar alguno de los argumentos, se hacen disponibles como parámetros posicionales $1, $2, ....

Desde dentro de un guión de shell puede utilizarse el comando set para especificar parámetros posicionales también. Por ejemplo,

	set - *
establecerá $1 como primer nombre de fichero en el directorio actual, $2 como el siguiente nombre de fichero, y así sucesivamente. Tenga presente que el primer argumento "-" asegura el tratamiento correcto cuando el primer nombre de fichero comienza con un caracter -.

3.1 Transmisión de parámetros

Al invocar un guión de shell, pueden suplirse tanto los parámetros posicionales como las palabras clave en dicha invocación. Los parámetros de palabra clave también pueden implicitarse a un guión de shell, especificando de forma adelantada la exportación de tales parámetros. Por ejemplo,
	export user box
demarca las variables user y box para su exportación. Al invocar un guión de shell, se realizan copias de todas las variables exportadas para su uso dentro del guión invocado. La modificación de tales variables dentro del guión no afecta los valores en la shell que lo ha invocado. Generalmente es verdadero que un guión de shell no modifica el estado de la shell que lo ha invocado sin la solicitud explícita por parte del shell invocante (los descriptores de fichero compartido son una excepción a esta regla).

Aquellos nombres cuyo valor se encuentran concebidos para permanecer constantes pueden ser declarados como de sólo lectura. La sintaxis de esta operación es la misma que aquella utilizada para el comando export,

	readonly nombre ...
De aquí en mas, intentos subsecuentes de establecer variables de sólo lectura son ilegales.

3.2 Sustitución de parámetros

Si un parámetro de shell no resulta establecido, entonces es sustituído por una cadena nula. Por ejemplo, si la varibale d no está configurada
	echo $d
o
	echo ${d}
presenta un eco nulo. Puede darse una cadena por defecto como en
	echo ${d-.}
que dará como eco el valor de la variable d si es establecida, y `.' si es lo contrario. La cadena por defecto es evaluada utilizando las convenciones de citado usuales, de modo que
	echo ${d-'*'}
dará como eco * si la variable d no está establecida. De forma similar
	echo ${d-$1}
dará como eco el valor de de d si está establecida y el valor de $1 (si lo tiene) en caso contrario. Una variable puede ser asignada a un valor por defecto utilizando la notación
	echo ${d=.}
que sustituye la misma cadena como
	echo ${d-.}
y si d no hubiese sido establecida previamente, entonces será establecida como la cadena `.'. (la notación ${...=...} no está disponible para los parámetros posicionales).

Si no hay un valor por defecto sensible, entonces la notación

	echo ${d?message}
dará eco el valor de la variable d si cuenta con uno, caso contrario mensaje resulta impreso por la shell, y la ejecución del guión de shell se abandona. Si mensaje está ausente, entonces se presenta un mensaje estándar. Un guión de shell que requiera que se establezcan algunos parámetros puede iniciar de la siguiente manera:
	: ${user?} ${acct?} ${bin?}
	...
Los dos puntos (:) es un comando incorporado en la shell y no hace nada una vez que sus argumentos han sido evaluados. Si alguna de las variables user, acct or bin no han sido establecidas, entonces la shell abandona la ejecución del procedimiento.

3.3 Sustitución de comandos

La salida estándar de un comando puede ser sustituída de manera similar a los parámetros. El comando pwd presenta en su salida estándar el nombre del directorio actual. Por ejemplo, si el directorio actual es /usr/pedro/bin entonces el comando
	d=`pwd`
es equivalente a
	d=/usr/pedro/bin
La cadena entera entre acentos graves (`...`) se toma como el comando a ejecutar, y es reemplazdo con la salida del comando. El comando es escrito utilizando las convenciones de citado convencionales, con la excepción que un ` debe ser escapado, usando un \. Por ejemplo,
	ls `echo "$1"`
es equivalente a
	ls $1
La sustitución de comandos ocurre en todos aquellos contextos donde ocurre también la sustitución de parámetros (incuyendo documentos here) y el tratamiento del texto resultante es el mismo en ambos casos). Este mecanismo permite utilizar los comandos de procesamiento de cadenas dentro de los guiones de shell. Un ejemplo de tales comandos lo constituye basename, el cual remueve de una cadena un sufijo especificado. Por ejemplo:
	basename main.c .c
presenta la cadena main. Su uso se ilustra por el siguiente fragmento como un comando cc.
	case $A in
	     ...
	     *.c)       B=`basename $A .c`
	     ...
	esac
que establece B como la parte de $A con el sufijo .c removido.

He aquí algunos ejemplos compuestos

· for i in `ls -t`; do ...
donde la varible i resulta establecida para los nombres de fichero en órden temporal, los más recientes primero.
· set `date`; echo $6 $2 $3, $4
presenta, por ejemplo, 1977 Nov 1, 23:59:59

3.4 Evaluación y citado

La shell es un procesador de macros que provee sustitución de parámetros, sustitución de comandos, y generación de nombres de fichero para los argumentos de comandos. Esta sección discute el orden en el cual dichas evaluaciones ocurren, y los efectos de los variados mecanismos de citado.

Los comandos son analizados inicialmente de acuerdo a la gramática dada en el Apéndice A. Antes de que un comando se ejecute, ocurren las siguientes sustiotuciones.

sustitución de parámetros, ej. $USER
sustitución de comandos, ej `pwd`
Solo se produce una evaluación, de modo que si - por ejemplo - el valor asignado a la variable X es la cadena $y, entonces
	echo $X
dará como eco $y.
interpretación de caracteres en blanco
Siguiendo a las sustituciones mencionadas, los caracteres resultantes resultan divididos en palabras no en blanco (interpretación de caracteres en blanco). En este cometido se interpreta en blanco a los caracters de la cadena $IFS. Por defecto, esta cadena consiste en un caracter en blanco, un tabulador, y una nueva línea. La cadena nula no es considerada como una palabra, a no ser de que esté citada. Por ejemplo:
	echo ''
pasará a la cadena nula como el primer argumento de echo, como en
	echo $null
llama a echo sin argumentos si la variable null no está establecida, o está establecida a una cadena nula.
generación de nombre de fichero
Cada palabra es luego analizada en busca de los caracteres de patrón de ficheros *, ? y [...] y resulta generada una lista alfabética de los nombres de ficheros para reemplazar la palabra. Cada uno de tales nombres de ficheros es un argumento separado.
Las evaluaciones recién descriptas también ocurren en la lista de palabras asociadas con un bucle for. Sólo ocurre la sustitución en la palabra usada para una ramificación case.

Junto con estos mecanismos de citados ya descriptos que utilizan \ y '...', se provee un tercer mecanismo de citado, que en el cual se utiliza entrecomillado "...". Dentro de las comillas se producen la sustitución de parámetros y de comandos, pero se produce la generación de nombre de ficheros ni la interpretación en blanco. Los caracteres a continuación guardan un significado especial en el entrecomillado, y pueden ser citados usando \.

$
sustitución de parámetros
`
sustitución de comandos
"
finaliza la cadena citada
\
cita los caracteres especiales $ ` " \
Por ejemplo,
	echo "$x"
pasa el valor de la variable x como un argumento simple de echo. De forma similar,
	echo "$*"
pasa los parámetros posicionales como un argumento simple; equivale a
	echo "$1 $2 ..."
La notación $@ es similar a $*, a excepción que esté citada:
	echo "$@"
pasa los parámetros posicionales (sin evaluarlos) a echo, y es equivalente a
	echo "$1" "$2" ...
La siguiente tabla indica los metacaracteres de la shell que resultan evaluados para cada uno de los mecanismos de citado.
                           Meta-caracter
                       \       $       *       `       "       '
               '       n       n       n       n       n       t
               `       y       n       n       t       n       n
               "       y       y       n       y       t       n

                       t       terminador
                       y       interpretado
                       n       nointerpretado
Figura 2. Mecanismos de citado
En los casos donde se requiere más de una evaluación de una cadena, se puede usar el comando incorporado de la shell eval. Por ejemplo, si la variable X tiene el valor $y, y si y tiene el valor pqr, entonces
	eval echo $X
da como eco la cadena pqr.

Por lo general el comando eval evalúa sus argumentos de la misma manera en que lo hacen todos los comandos, y trata el resultado como una entrada al shell. La entrada es leída y el/los comando/s resultantes son ejecutados. Por ejemplo

	wg=\'eval who|grep\'
	$wg pedro
equivale a
	who|grep pedro
En dicho ejemplo, se requiere usar eval ya que sucedida la sustitución efectuada, no existe intepretación de meta-caracteres, tal como en |.

3.5 Manejo de Error

El tratamiento de los errores detectados por la shell dependen del tipo de error y si la shell está siendo utilizada según su modalidad interactiva. Una shell interactivca es una donde la entrada y salida se encuentran conectadas a un terminal (como lo determina gtty (2)). Una shell invocada con el flag -i también es interactiva.

La ejecución de un comando (ver también 3.7) da fallo por cualquiera de los siguientes motivos:

En todos estos casos la shell continúa ejecutando comandos subsiguientes. Un mensaje de error resulta presentado por la shell, excepto para el último caso. Todos los errores restantes provocan que la shell salga de un procedimiento de comandos. Una shell interactiva devolverá para leer otro comando desde el terminal. Tales errores incluyen los siguientes: El flag -e de la shell provoca que la shell termine si detecta cualquier error.
1
hangup
2
interrupt
3*
quit
4*
illegal instruction
5*
trace trap
6*
IOT instruction
7*
EMT instruction
8*
floating point exception
9
kill (cannot be caught or ignored)
10*
bus error
11*
segmentation violation
12*
bad argument to system call
13
write on a pipe with no one to read it
14
alarm clock
15
software termination (from kill (1))
Figura 3. Señales de UNIX
Aquellas señales marcadas con un asterisco producen un volcado de núcleo si no son atrapadas. Sin embargo, la shell en si misma ignora a quit, la cual es la única señal externa que puede provocar un volcado de núcleo. Las señales en esta lista de interés potencial para los programas de la shell son 1, 2, 3, 14 y 15.

3.6 Manejo de Fallas

Los guiones de shell normalmente terminan cuando se recibe una interrupción desde el terminal. El comando trap se utiliza si se requiere alguna limpieza, tal como remover ficheros temporales. Por ejemplo,
	trap 'rm /tmp/ps$$; exit' 2
establece una trampa para la señal 2 (interrupción de terminal), y si esta señal es recibida, ejecutará los comandos
	rm /tmp/ps$$; exit
exit es otro comando incorporado de la shell que terminal la ejecución de un guión de shell. El exit se requiere; de lo contrario, luego de que se ha establecido la trampa, la shell continuará ejecutando el procedimiento en el lugar en donde fue interrumpido.

Las señales de UNIX pueden ser manejadas en una de tres manearas. Pueden ser ignoradas, en cuyo caso la seña nunca se envía al proceso. Pueden se atrsapadas, en cuyo caso el proceso debe decidir qué acción tomar cuando se recibe la señal. Finalmente, pueden ser dejadas para provocar la terminación del proceso sin que este deba tomar mayor acción. Si una señal está siendo ignorada en una etrada al guión de shell, por ejemplo, invocándolo en el segundo plano (ver 3.7), entonces los comandos trap (y la señal) resultan ignoradas.

El uso de trap se ilustra por esta versión modificada del comando touch (Figura 4). La acción de limpieza es remover el fichero basura$$.

	flag=
	trap 'rm -f basura$$; exit' 1 2 3 15
	for i
	do case $i in
	   -c)  flag=N ;;
	   *)   if test -f $i
		then    ln $i basura$$; rm basura$$
		elif test $flag
		then    echo fichero \'$i\' no existe
		else    >$i
		fi
	   esac
	done
Figura 4. El comando touch
El comando trap aparece antes de la creacion del fichero temporal; de otro modo sería posible para el proceso, morir sin remover el fichero.

Ya que no hay señal 0 en UNIX, esta es utilizada por la shell para indicar que los comandos sean ejecutados a la salida del guión de shell.

Un procedimiento puede, por si mismo, elegir ignorar señales especificando la cadena nula como el argumento para atrapar. El siguiente fragmento se toma del comando nohup.

	trap '' 1 2 3 15
que causa que hangup, interrupt, quit, y kill sean ignoradas tanto por el procedimiento y por los comandos invocados.

Las trampas pueden resetearse diciendo

	trap 2 3
que resetea las trampas para las señales 2 y 3 a sus valores por defecto. Puede obtenerse una listado de los valores actuales de las trampas escribiendo
	trap
El procedimiento scan (Figura 5) es un ejemplo del uso de trap donde no hay salida en el comando trap. scan toma cada directorio en el directorio actual, solicita su nombre, y luego ejecuta los comandos mecanografiados en el terminal hasta el final del fichero o se reciba una interrupción. Las interrupciones son ignoradas mientras se ejecutan los comandos solicitados, pero provocan su terminación cuando scan aguarda entrada.
	d=`pwd`
	for i in *
	do if test -d $d/$i
	   then cd $d/$i
		while echo "$i:"
		      trap exit 2
		      read x
		do trap : 2; eval $x; done
	   fi
	done
Figura 5. El comando scan
read x es un comando incorporado de la shell que lee una línea desde la entrada estándar y coloca el resultado como valor de la variable x. Deuvuelve un status de salida no cero tanto si se recibe una lectura de Fin-de-Fichero o una interrupción.

3.7 Ejecución de comandos

Para ejecutar un comando (otros que los incorporados de la shell), esta inicialmente crea un nuevo proceso utilizando la llamada de sistema fork. El ambiente de ejecución para el comando incluye entrada, salida, y los estados de señales, y se establece en el proceso hijo antes de que sea ejecutado el comando. El comando incorporado de la shell exec se usa en los raros casos donde no se requiere fork, y simplemente reemplaza la shell con un nuevo comando. Por ejemplo, una versión más simple de comando nohup podría ser
	trap \'\' 1 2 3 15
	exec $*
El trap desactiva las señales especificadas de manera que sean ignoradas por los comandos creados subsecuentementes, y exec reemplaza la shell por el comando especificado.

La mayoría de las maneras de redirección de entrada/salida ya han sido descriptas. La siguiente palabra es sólo sujeta a sustitución de parámetro y comando. No tiene generación de nombre de fichero o interpretación de caracter en blanco, de moque que, por ejemplo

	echo ... >*.c
escribirá su salida en un fichero cuyo nombre es *.c. Las especificaciones de entrada/salida son evaluadas de izquierda a derecha en la tal como aparecen en el comando.
> palabra
La salida estándar (descriptor de fichero 1) es enviado al fichero palabra, el cual es creado si no existe ya.
>> palabra
La salida estándar es enviado al fichero palabra. Si el fichero existe, entonces la salida es agregada (buscando su final); de otra forma, se crea el fichero.
< palabra
La entrada estándar (descriptor de fichero 0) es tomada desde el fichero palabra.
<< palabra
La entrada estándar es tomada desde las líneas de entrada de la shell que siguen pero que no incluyen una línea consistente solo de palabra. Si palabra es citado, entonces no ocurre interpretación del documento. Si palabra no está citado, entonces ocurren la sustitución de parámetros y comandos y se usa \ para citar los caracteres \ $ ` y el primer caracter de palabra. En el último caso,, se ignora el caracter \newline (refiere a cadenas citadas).
>& digit
El descirptor de fichero digit resulta duplicado usando la llamada de sistema dup (2) y el resultado se usa como salida estándar.
<& digit
La entrada estándar es duplicada desde el descriptor de fichero digit.
<&-
La entrada estándar es cerrada.
>&-
La salida estándar es cerrada.
Cualquiera de lo expuesto anteriormente puede ser precedido por un dígito en cuyo caso el descriptor de fichero creado es aquél especificado por el dígito en lugar de los 0 o 1 por defecto. Por ejemplo,
	... 2>fichero
corre un comando con el mensaje de salida (file descriptor 2) dirigido a fichero.
	... 2<&1
corre un comando con su salida estándar y un mensaje de salida unificado. (estrictamente hablando, el descriptor de fichero 2 es creado duplicando el descriptor de fichero 1, pero el efecto es usualmente unificar las dos streams)

El ambiente para que un comando que corre en segundo plano como

	list *.c | lpr &
es modificado en dos maneras. Primeramente, la entrada estándar por defecto para tales comandos es el fichero vacío /dev/null. Esto impide dos procesos (la shell y el comando), que están corriendo en paralelo, de intentar leer de la misma entrada. Se produciría caos si esto no fuese el caso. Por ejemplo,
	ed file &
permitiría tanto al editor y a la shell leer desde la misma entrada al mismo tiempo.

La otra modificación al ambiente de un comando en segundo plano es desconectar las señales QUIT e INTERRUPT de modo que sean ignoradas por el comando. Esto permite a estas señales ser utilizadas en el terminal sin provicar que el comando en segundo plano termine. Por esta razón la convención UNIX para una señal es que si está establecida en 1 (ignorada), entonces nunca cambia, incluso por un período corto de tiempo. Tenga presente que el comando trap de la shell no tiene efecto para una señal ignorada.

3.8 Invocando la shell

Las siguientes flags son intepretadas por la shell cuando ésta es invocada. Si el primer caracter del argumento cero es un -, entonces los comandos son leídos desde el fichero .profile.
-c string
Si el flag -c está presente, entonces los comandos son leídos desde string.
-s
Si el flag -s está presente, o si no quedan argumentos, entonces los comandos son leídos desde la entrada estándar. La salida de la shell es escrita al decriptor 2 de fichero.
-i
Si el flag -i está presente o si la entrada de la shell y la salida están asociadas al terminal (como lo indica gtty), entonces esta shell es interactiva. En este caso TERMINATE resulta ignorado (de modo que kill 0 no detiene una shell interactiva, e INTERRUPT es atrapado e ignorado (de modo que wait es ininterrumpible. EN todos los casos QUIT es ignorada por la shell.

Reconocimientos

El diseño de la shel se basa en parte en el shell UNIX original, y en la shell de PWB/UNIX. Algunas funcionalides han sido tomadas de ambos. Existen también similitudes con el intéprete de comandos del Sistema de Acceso Múltiple de Cambridge (CMAS) y el CTSS.

Quisiera agradecer a Dennis Ritchie y a John Mashey por muchas discusiones durante el diseño de la shell. También estoy agradecido con los miembnros del Centro de Investigación de la Ciencia del Cómputo y con Joe Maranzano por sus comentarios en el borrador de este documento.

Appendix A - Gramática

item:
palabra
input-output
name = value
simple-command:
item
simple-command item
command:
simple-command
( lista-de-comandos )
{ lista-de-comandos }
for name do lista-de-comandos done
for name in palabra ... do lista-de-comandos done
while lista-de-comandos do lista-de-comandos done
until lista-de-comandos do lista-de-comandos done
case palabra in case-part ... esac
if lista-de-comandos then lista-de-comandos else-part fi
pipeline:
command
pipeline | command
andor:
pipeline
andor && pipeline
andor || pipeline
lista-de-comandos:
andor
lista-de-comandos ;
lista-de-comandos &
lista-de-comandos ; andor
lista-de-comandos & andor
input-output:
> file
< file
>> palabra
<< palabra
file:
palabra
& digit
& -
case-part:
pattern ) lista-de-comandos ;;
pattern:
palabra
pattern | palabra
else-part:
elif lista-de-comandos then lista-de-comandos else-part
else lista-de-comandos
empty
empty:
palabra:
una secuencia de caracteres no nulos
name:
a sequence of letters, digits or underscores starting with a letter
digit:
0 1 2 3 4 5 6 7 8 9

Apéndice B - Meta-caracteres y Palabras Reservados

a) sintácticos
|
símbolo de caño
&&
símbolo `andf'
||
símbolo `orf'
;
separador de comandos
;;
delimitador de case
&
comandos en segundo plano
( )
agrupamiento de comandos
<
redirección de entrada
<<
entrada desde un documento here
>
creación de salida
>>
agregado de salida
b) patrones
*
coincide cualquier caracter incluyendo nulos
?
coincide cualquier caracter único
[...]
coincide cualquiera de los caracteres entrecorchetados
c) substitución
${...}
substituye variable de la shell
`...`
substituye salida de comando
d) citado
\
cita el siguiente caracter
'...'
cita los caracteres entre acentos greaves, excepto lo '
"..."
cita los caracteres entrecomillados, excepto para $ ` \ "
e) palabras reservadas
if then else elif fi
case in esac
for while until do done
{  }

Esta es una versión en HTML del tuturial del Shell original de Steve Bourne. Era Eriksson encontró la fuente en http://cm.bell-labs.com/7thEdMan/vol2/shell.bun y generó su propia copia con troff, que luego editó como una versión HTML.

Al descontinuarse la copia localizada en www.iki.fi/era/unix/shell.html, en enero de 2012 realicé una copia local. En e ste documento las referencias a la primer persona fueron reemplazados por el nombre del autor probable de la página, y se ha insertado esta nota en imprenta pequeña. La página original ha sido archivada a través de WebCite como http://www.webcitation.org/64dH90Kic.