#LyX 1.3 created this file. For more info see http://www.lyx.org/ \lyxformat 221 \textclass article \language spanish \inputencoding auto \fontscheme default \graphics default \paperfontsize default \spacing single \papersize Default \paperpackage a4 \use_geometry 0 \use_amsmath 0 \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle default \layout Title Programación en Shell (bash/Bourne) \layout Author Rodrigo Gallardo--- lgallardo@computacion.cs.cinvestav.mx \newline Gunnar Wolf --- gwolf@gwolf.cx \layout Date 14 de febrero, 2002 \layout Abstract En este tutorial veremos desde los aspectos básicos del Shell hasta cómo utilizarlo como un lenguaje completo de propósito general. \layout Abstract Asumimos que el asistente al tutorial tiene conocimientos básicos de uso de un sistema Unix, si bien puede nunca haberse internado al maravilloso mundo de la programación. \layout Standard \begin_inset LatexCommand \tableofcontents{} \end_inset \layout Section Introducción \layout Subsection Las computadoras antiguas \layout Standard Para poder comprender la funcion del shell es necesario comprender el proceso historico del computo, desde las primeras computadoras hasta los sistemas Unix modernos. \layout Standard Las primeras computadoras, en los 40s y 50s, no tenian contemplada la ejecucion de mas que un solo programa --- Eran programadas en un principio fijando a mano interruptores, y fue un muy gran avance para su usabilidad la introducci on de las primeras lectoras de tarjetas perforadas, pues, ademas de reducir sensiblemente el tiempo muerto en que un programa tenia que ser introducido, permitia guardarlo para uso futuro de una manera conveniente. \layout Subsubsection Procesamiento por lotes \layout Standard Tras la aparicion de las lectoras de tarjetas, fue solo cuestion de tiempo el que se serializaran procesos en lotes --- Cada usuario dejaba listo su programa en la lectora, la cual lo alimentaba a la computadora, esperaba a que ejecutara e imprimiera resultados, y continuaba con el siguiente proceso. Al automatizarse la carga, los tiempos muertos para la computadora se redujeron al minimo. \layout Subsubsection Concurrencia primitiva \layout Standard Ahora, el uso del procesador se podia aprovechar aun mas: El tiempo en que la lectora alimentaba a la memoria central o en que se imprimian los resultados era, a todas luces, desperdiciado. Ademas, habia trabajos de alta prioridad, que debian esperar su turno como cualquier otro pese a su importancia. \layout Standard Si bien antes de la concurrencia ya existian sistemas operativos pequenos y limitados encargados de una abstraccion basica del hardware, su rol como asignadores de recursos nace cuando hay varios programas simultaneos en ejecucion. Y al haber ya un complejo ambiente en el que diferentes usuarios desde diferentes consolas requieren cargar y ejecutar diferentes programas a la vez, nace la necesidad real de un programa base que permita al usuario especificar a la computadora que quiere hacer --- un shell. \layout Subsection El shell como un lanzaprogramas \layout Standard Un shell basico es simplemente un lanzaprogramas. Acepta ordenes del usuario, las cuales se traducen directamente en el nombre de un programa a ejecutar. Claro, es necesario proveer al usuario tambien de los comandos basicos para manejar los archivos en la computadora, ya que un sistema con concurrencia , para ser eficiente, requiere tener espacio de almacenamiento permanente para los programas ---un disco--- y, claro, proveer los mecanismos para administrarlo --- crear, copiar, eliminar, compilar, etc. \layout Subsection El shell de hoy \layout Standard Ha pasado ya mucho tiempo desde aquellos primitivos shells. En un sistema Unix moderno, el shell es ya un entorno completo de programacion, proporcionando al usuario todas las herramientas necesarias para automatizar la administracion de sistemas, facilitar diversas labores cotidianas, e incluso jugar con un ambiente de desarrollo agradable. \layout Standard En Unix hay muchos diferentes shells, y los usuarios de cada uno de ellos lo defienden como el mejor con fervor religioso (lo cual no debe de sorprender a nadie Las principales familias son el Korn Shell, el C Shell y el Bourne Shell. Cada una de estas familias tiene varias implementaciones, y para todos hay cuando menos una implementacion libre. En este tutorial nos enfocamos al Bourne Shell, recomendando el uso de \emph on bash \emph default (Bourne Again Shell), por ser el que mas gente encuentra como primera experienc ia en un sistema Unix al ser el predeterminado de casi cualquier Linux, y por existir en todos los demas Unixes. \layout Section Sintaxis general \layout Standard Usar el shell como un lanzaprogramas es muy simple: Cada que le damos una linea, ejecuta el comando cuyo nombre le escribimos. Podemos indicar la ruta completa al archivo, o ejecutar los archivos que aparezcan en el path de ejecucion (ver seccion \begin_inset LatexCommand \ref{var. ambiente} \end_inset ). \layout Subsubsection Flujos - STDIN, STDOUT, STDERR \layout Standard En el shell -al igual que en cualquier programa de consola de Unix--- tenemos tres flujos o descriptores de archivo abiertos por default: La entrada estandar (STDIN), la salida estandar (STDOUT) y el error estandar (STDERR). El primero puede ser utilizado para leer de el, y los otros dos para enviar datos hacia ellos. Tipicamente, STDIN viene del teclado de la terminal actualmente en uso, y tanto STDOUT como STDERR van hacia su pantalla. STDOUT muestra los datos normales o esperados durante la ejecucion, y STDERR se utiliza para enviar informacion de depuracion y errores. Cualquier programa iniciado desde el shell, a menos que se lo indiquemos explicitamente, hereda estos tres descriptores de archivo, permitiendole interactuar con el usuario. \layout Subsection Más allá del lanzaprogramas \layout Standard Si bien su principal mision es ser un lanzaprogramas, un shell tiene una funcionalidad mucho mayor. En Unix, muchas veces se utiliza al shell para interconectar programas independientes ---recuerden la filosofia Unix, que nos da una gran cantidad de herramientas que hacen solamente una cosa simple, pero permiten interactuar con otros programas similares creando asi construcciones complejas y utiles. El shell ademas da facilidades al usuario como la expansion, el \emph on globbing \emph default y los aliases. \layout Subsubsection Redirecciones (>, <, >>, &>, &>>) \layout Description >,\SpecialChar ~ >> La salida de un programa muchas veces es util como tal enviada a pantalla. Sin embargo, muchas veces esta salida puede ser mucho mayor de lo que podemos manejar a ojo. Tal vez queremos generar, por ejemplo, el listado completo de archivos bajo cierta parte del arbol. Para enviar la salida estandar de un comando a un archivo usamos > (para borrar cualquier contenido previo del archivo) o >> (para agregar nuestros datos al final del archivo). Por ejemplo, \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ls -R /usr > /tmp/archUsr \begin_inset ERT status Collapsed \layout Standard } \end_inset crea un archivo \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset /tmp/archUsr \begin_inset ERT status Collapsed \layout Standard } \end_inset con el listado de archivos bajo /usr. \layout Description < Con esto podemos indicar a un proceso que tome su entrada estandar de un archivo existente. Por ejemplo, si quiero ejecutar de manera automatica un proceso que siempre me pide confirmacion durante la ejecucion, puedo grabar en un archivo todos los comandos que este espera, e invocarlo asi: \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset /home/gwolf/miprog < /home/gwolf/miprog.comandos \begin_inset ERT status Collapsed \layout Standard } \end_inset (claro esta, esto puede ser peligroso. Cuando un proceso requiere confirmacion, normalmente es por alguna buena razon. Sin embargo, les pido que me lo valgan como ejemplo). \layout Description &>,\SpecialChar ~ &>> Esto es equivalente a lo que mencionabamos respecto a la salida estandar, pero aplicado al error estandar. Por ejemplo, si quiero revisar los errores resultantes de ejecutar un proceso determinado, puedo enviarlos a un archivo, de la siguiente manera: \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset tar czvf archivo.tar.gz /usr /var /lib &> erroresTar \begin_inset ERT status Collapsed \layout Standard } \end_inset me indicara en el archivo erroresTar cualquier problema que se me pueda presentar, como permisos incorrectos. \layout Standard Ahora, cabe recordar aqui que en Unix los dispositivos son representados por y tratados como un archivo. Por tanto, \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ls -l > /dev/ttyS0 \begin_inset ERT status Collapsed \layout Standard } \end_inset enviara un listado de archivos al primer puerto serial. \layout Subsubsection Pipes (|, &|) \layout Standard Muchas veces necesito pasar a un proceso la salida de otro. Por ejemplo, para contar la cantidad de lineas en un archivo sin repeticion, primero ordeno el archivo (con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset sort \begin_inset ERT status Collapsed \layout Standard } \end_inset ), despues elimino lineas duplicadas (con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset uniq \begin_inset ERT status Collapsed \layout Standard } \end_inset ) y por ultimo cuento las lineas (con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset wc \begin_inset ERT status Collapsed \layout Standard } \end_inset ). Si bien podria hacerlo de esta manera: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset sort archivo > /tmp/ordenado \layout Quote uniq /tmp/ordenado > /tmp>unico \layout Quote wc /tmp/unico \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard esto claramente no es optimo. Puedo, mejor, \emph on entubar \emph default la salida estandar de un proceso y enviarlo al siguiente utilizando los \emph on pipes \emph default , simbolizados con el caracter \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset | \begin_inset ERT status Collapsed \layout Standard } \end_inset , de esta manera: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset sort archivo | uniq | wc \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Lo cual, ademas de mas compacto, es mas facil de leer y entender. \layout Standard Si quiero redireccionar el error estandar en vez de la salida estandar, puedo usar \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset &| \begin_inset ERT status Collapsed \layout Standard } \end_inset . \layout Subsubsection Control de procesos (&) \layout Standard Muchas veces iniciamos procesos, como la descompresion de un .tar con muchos archivos o la transferencia de un archivo remoto, que pueden tomar mucho tiempo y que no nos interesa la respuesta que puedan enviar a la consola, sino que su resultado final. Aprovechando que Unix es un sistema multitareas, podemos enviar cualquier comando que ejecutemos a un segundo plano agregando \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset & \begin_inset ERT status Collapsed \layout Standard } \end_inset al final de la linea de comando, de la siguiente manera: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset $ wget http://iso.softwarelibre.org.mx/debian-2.2r5-1.iso & \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Podemos lograr este mismo efecto si, una vez lanzado el comando, lo suspendemos con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ^Z \begin_inset ERT status Collapsed \layout Standard } \end_inset y lo enviamos a ejecucion en segundo plano con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset bg \begin_inset ERT status Collapsed \layout Standard } \end_inset : \layout Standard \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset $ wget http://iso.sofwarelibre.org.mx/debian-2.2r5-1.iso \layout Standard \emph on ^Z \layout Standard [1]+ Stopped wget http://iso.softwarelibre.org.mx/debian-2.2r5-1.iso \layout Standard $ bg \layout Standard [1]+ Stopped wget http://iso.softwarelibre.org.mx/debian-2.2r5-1.iso & \layout Standard $ \layout Standard \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Subsubsection \emph on Globbing \emph default y expansión ({}, ~) \layout Standard El shell es un gran aliado cuando se trata de escribir menos. Hay varios mecanismos para ahorrarnos teclazos: \layout Standard \emph on Globbing \emph default es probablemente el mas comun, el que todos conocemos --- La expansion de nombres usando el caracter \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset * \begin_inset ERT status Collapsed \layout Standard } \end_inset , que significa \begin_inset Quotes eld \end_inset todo lo que puedas acomodar ahi \begin_inset Quotes erd \end_inset . Por ejemplo, si en un directorio tengo los archivos \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset salida.tmp, asdf, otroarchivo \begin_inset ERT status Collapsed \layout Standard } \end_inset y \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset prueba \begin_inset ERT status Collapsed \layout Standard } \end_inset , y en ese directorio corro \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cat * \begin_inset ERT status Collapsed \layout Standard } \end_inset , el shell lo interpretara como si hubiera escrito \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cat asdf otroarchivo prueba salida.tmp \begin_inset ERT status Collapsed \layout Standard } \end_inset (en orden alfabetico). Si le pongo \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cat o* \begin_inset ERT status Collapsed \layout Standard } \end_inset , procesara unicamente los archivos que inician con o, y dara por tanto \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cat otroarchivo \begin_inset ERT status Collapsed \layout Standard } \end_inset . \layout Standard La expansion trabaja con argumentos mucho mas claramente definidos que el globbing. La expansion esta claramente hecha para ahorrar teclazos y hacer mas inteligent e al shell. \layout Standard Cuando especificamos una lista de valores separada por comas entre llaves, el shell la expande, convirtiendola en la cadena con cada uno de los argumentos. Por ejemplo, \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo un/path/{algo,muy,demasiado}/largo \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Nos produce la siguiente salida: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset un/path/algo/largo un/path/muy/largo un/path/demasiado/largo \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Ahora, tenemos que acordarnos de un par de reglas del juego: \layout Itemize La expansión va sobre una sóla palabra. Si ponemos: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo un texto {muy,algo} confuso \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Quote obtendremos como resultado: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset un texto muy algo confuso \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Itemize La expansión no se efectúa dentro de comillas, ni siquiera dobles (ver \begin_inset LatexCommand \ref{cadenas} \end_inset ), por lo que no sirve que intentemos corregir de esta manera el ejemplo anterior: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo \begin_inset Quotes eld \end_inset un texto {muy,algo} confuso \begin_inset Quotes erd \end_inset \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Quote pues nos dará por resultado: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset un texto {muy,algo} confuso \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Itemize Si escapamos los espacios, dejan de ser delimitadores y se convierten en parte de una palabra. Por tanto, podemos hacer: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo un \backslash texto \backslash {muy,algo} \backslash confuso \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Quote y obtendremos: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset un texto muy confuso un texto algo confuso \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Itemize Podemos tener múltiples expansiones en una sóla línea. Estas se expandirán generando todas las posibles combinaciones: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo {Un,Otro} \backslash texto \backslash {muy,algo} \backslash confuso. \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Quote nos da: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset Un texto muy confuso. Un texto algo confuso. Otro texto muy confuso. Otro texto algo confuso. \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Subsubsection Aliases \layout Standard Podemos indicarle al shell que cada que le demos determinada cadena la substituy a por otra. Para esto utilizamos el comando interno \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset alias \begin_inset ERT status Collapsed \layout Standard } \end_inset . \layout Standard Cuando el shell ejecuta cualquier comando, revisa si la \emph on primera \emph default palabra de cada comando que le demos, y si la encuentra en su tabla de aliases (y el operador no requiere que se inteprete literalmente usando comillas) la substituye antes de continuar procesando la línea. \layout Standard Para crear un alias podemos hacerlo de la siguiente manera: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset alias cosa='ls -l' \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard con lo que cada que indiquemos el comando \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cosa \begin_inset ERT status Collapsed \layout Standard } \end_inset el shell ejecutará \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ls -l \begin_inset ERT status Collapsed \layout Standard } \end_inset . Ahora, si le pedimos \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo cosa \begin_inset ERT status Collapsed \layout Standard } \end_inset , como no es la primera palabra, nos va a regresar a secas \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cosa \begin_inset ERT status Collapsed \layout Standard }. \end_inset \layout Standard Ahora, si indicamos lo siguiente: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo `cosa` \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard nos va a dar el \emph on resultado de ejecutar \emph default \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ls -l \begin_inset ERT status Collapsed \layout Standard } \end_inset . Recuerda que lo que encerremos en comillas inversas (ver \begin_inset LatexCommand \ref{comillas-inversas} \end_inset ) es ejecutado en un sub-shell, y aquel sub-shell ve a \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset cosa \begin_inset ERT status Collapsed \layout Standard } \end_inset como la primera palabra del comando. \layout Standard Al tener los aliases precedencia aún sobre los comandos internos del shell, son muy útiles para ahorrarnos teclazos. Por ejemplo, mucha gente tiene los siguiente aliases definidos: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset alias ls='ls --color' \newline alias rm='rm -i' \newline alias mv='mv -i' \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard con lo que sin tener que agregarle opciones, siempre le pasaremos \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset --color' \begin_inset ERT status Collapsed \layout Standard } \end_inset al \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset ls \begin_inset ERT status Collapsed \layout Standard } \end_inset , y siempre eliminaremos y moveremos requiriendo confirmar. \layout Standard Para consultar qué aliases tenemos definidos, damos \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset alias \begin_inset ERT status Collapsed \layout Standard } \end_inset sin argumentos. \layout Standard Para eliminar un alias, damos \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset unalias \begin_inset ERT status Collapsed \layout Standard } \end_inset . \layout Subsection Bourne y bash vs. otros shells \layout Standard En Unix tenemos muchos diferentes shells. ¿Por qué recomendamos elegir a los derivados del Bourne? \layout Subsubsection Edición de comandos \layout Subsubsection Historia \layout Subsection Variables y cadenas \layout Standard Prácticamente cualquier lenguaje de programación nos proporciona variables con las que podemos trabajar. En shell manejarlas sigue unas reglas un tanto particulares. \layout Subsubsection Variables en general \layout Standard En shell, los nombres de variables pueden contener letras, números y guiones bajos. Si bien ninguna regla lo marca, es una convención muy común que los nombres de las variables vayan completamente en mayúsculas. No es necesario declarar de qué tipo será cada variable (al shell le da igual si las variables guardan cadenas o números), pero sí tenemos que comprender bien cómo manejarlas, pues es una fuente muy frecuente de errores el referirnos a una variable cuando requerimos su contenido, o a la inversa. \layout Standard Para asignar un valor a una variable lo hacemos de esta manera: \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset VARIABLE=valor \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Cuando queramos utilizar el valor de la variable podemos hacerlo anteponiendo a su nombre un signo \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset $ \begin_inset ERT status Collapsed \layout Standard } \end_inset : \layout Quote \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset echo $VARIABLE \begin_inset ERT status Collapsed \layout Standard } \end_inset \layout Standard Siempre que queramos imprimir, comparar, hacer cuentas o en general utilizar el valor \emph on contenido \emph default en la variable, lo haremos refiriéndonos a su valor con \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset $ \begin_inset ERT status Collapsed \layout Standard } \end_inset . Siempre que queramos indicar al shell que haga algo \emph on utilizando la variable \emph default , lo haremos refiriéndonos a su nombre, sin \begin_inset ERT status Collapsed \layout Standard { \backslash tt \end_inset $ \begin_inset ERT status Collapsed \layout Standard } \end_inset . \layout Subsubsection \begin_inset LatexCommand \label{var. ambiente} \end_inset Variables de ambiente \layout Subsubsection \begin_inset LatexCommand \label{cadenas} \end_inset Cadenas e interpolación \layout Section Control de flujo \layout Standard Como en cualquier lenguaje de programación, un 'script' de shell tiene que poder decidir que acciones tomar según el resultao operaciones anteriores. Además, es necesario automatizar el repetir acciones, ya sea un número fijo de veces, o hasta que se cumpla alguna condición. \layout Subsubsection Estados de salida \layout Standard Puesto que la función primaria del shell es ejecutar a otros programas, resulta natural desear controlar la ejecución de un script de acuerdo al resultado de la ejecución de estos. Para lograr eso, cada programa que se ejecuta en un sistema UNIX devuelve al programa que lo ejecutó un número, que representa el resultado obtenido. Si el programa se ejecutó sin errores, devuelve un cero. Si hubo algún error, devuelve un número distinto de cero. Este número depende del error especifico, y por lo tanto varía de programa a programa. Siempre que el shell necesita tomar una decisión basada en el resultado de otro programa, considera como 'cierto' a un valor 0, y como 'falso' a cualquier otro. \layout Subsection Operadores \layout Subsubsection Booleanos (&& || ! ) \layout Standard Los operadores booleanos permiten combinar los resultados de varias pruebas. Funcionan de forma identica a los de C. En particular, \family typewriter && \family default y \family typewriter || \family default evaluan sus argumentos de izquierda a derecha, deteniendose en cuanto se sabe el resultado total. Esto permite efectuar combinaciones de control sencillas. Por ejemplo, cuando se compila e instala un paquete de software, es común dar el comando \family typewriter make \family default seguido de \family typewriter make install \family default , si no hubieron errores. Es posible automatizar esta secuencia, dando el comando \family typewriter make&&make install \family default que efectuara al segundo solo si el primero termina sin errores. \layout Standard El operador \family typewriter ! \family default invierte el sentido del valor de retorno de un programa. \layout Standard Estos operadores se usan para encadenar pruebas, en particular aquellas que involucran el estado de retorno de un programa. \layout Subsubsection De archivo (test - \emph on x \emph default ) \layout Standard Para efectuar otras pruebas el shell provee el operador \family typewriter test \family default . Este permite realizar una serie de pruebas acerca del sistema de archivos, así como pruebas que dependan del valor de las variables del shell. Estos operadores son: \layout Standard De archivo: \layout LyX-Code -b ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un dispositivo de bloque. \layout LyX-Code \layout LyX-Code -c ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un dispositivo de caracteres. \layout LyX-Code \layout LyX-Code -d ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un directorio. \layout LyX-Code \layout LyX-Code -e ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO existe. \layout LyX-Code \layout LyX-Code -f ARCHIVO \layout LyX-Code Verdadero si ARCHIVO existe y es un archivo normal. \layout LyX-Code \layout LyX-Code -g ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO tiene encendido el bit sgid. \layout LyX-Code \layout LyX-Code -h ARCHIVO \layout LyX-Code -L ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un vínculo simbolico. \layout LyX-Code \layout LyX-Code -k ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO tiene encendido el bit 'sticky'. \layout LyX-Code \layout LyX-Code -p ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un 'named pipe'. \layout LyX-Code \layout LyX-Code -r ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es legible por este usuario. \layout LyX-Code \layout LyX-Code -s ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO existe y es no vacio. \layout LyX-Code \layout LyX-Code -S ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO es un 'socket'. \layout LyX-Code \layout LyX-Code -t FD \layout LyX-Code Verdadero si el descriptor FD esta abierto a una terminal. \layout LyX-Code \layout LyX-Code -u ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO tiene prendido el bit 'suid'. \layout LyX-Code \layout LyX-Code -w ARCHIVO \layout LyX-Code Verdadero si el usuario tiene permiso de escribir en el ARCHIVO. \layout LyX-Code \layout LyX-Code -x ARCHIVO \layout LyX-Code Verdadero si el usuario tiene permiso de ejecutar el ARCHIVO. \layout LyX-Code \layout LyX-Code -O ARCHIVO \layout LyX-Code Verdadero si el usuario es el dueño del ARCHIVO. \layout LyX-Code \layout LyX-Code -G ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO pertenece al grupo del usuario. \layout LyX-Code \layout LyX-Code -N ARCHIVO \layout LyX-Code Verdadero si el ARCHIVO ha sido modificado desde la última lectura. \layout LyX-Code \layout LyX-Code ARCHIVO1 -nt ARCHIVO2 \layout LyX-Code Verdadero si el ARCHIVO1 es más nuevo que el ARCHIVO2 \layout LyX-Code (de acuerdo a la fecha de modificación). \layout LyX-Code \layout LyX-Code ARCHIVO1 -ot ARCHIVO2 \layout LyX-Code Verdadero si el ARCHIVO1 es más viejo que el ARCHIVO2. \layout LyX-Code \layout LyX-Code ARCHIVO1 -ef ARCHIVO2 \layout LyX-Code Verdadero si el ARCHIVO1 es un vínculo duro al ARCHIVO2. \layout Standard Operadores de cadenas: \layout LyX-Code -z CADENA \layout LyX-Code Verdadero si la CADENA es vacia. \layout LyX-Code \layout LyX-Code -n CADENA \layout LyX-Code CADENA \layout LyX-Code Verdadero si la CADENA es no vacia. \layout LyX-Code \layout LyX-Code CADENA1 = CADENA2 \layout LyX-Code Verdadero si las cadenas son iguales. \layout LyX-Code \layout LyX-Code CADENA1 != CADENA2 \layout LyX-Code Verdadero si las cadenas son distintas. \layout LyX-Code \layout LyX-Code CADENA1 < CADENA2 \layout LyX-Code Verdadero si la CADENA1 va antes que la CADENA2 en orden lexicográfico. \layout LyX-Code \layout LyX-Code CADENA1 > CADENA2 \layout LyX-Code Verdadero si la CADENA1 va despues que la CADENA2 en orden lexicográfico. \layout Standard Otros operadores: \layout LyX-Code -o OPCION \layout LyX-Code Verdadero si la opcion del shell OPCION esta activada. \layout LyX-Code \layout LyX-Code ! EXPR \layout LyX-Code Verdadero si la EXPR es falsa. \layout LyX-Code \layout LyX-Code EXPR1 -a EXPR2 \layout LyX-Code Verdadero si ambas expresiones son verdaderas. \layout LyX-Code \layout LyX-Code EXPR1 -o EXPR2 \layout LyX-Code Verdadero si alguna expresión es verdadera. \layout LyX-Code \layout LyX-Code arg1 OP arg2 \layout LyX-Code Pruebas aritméticas. OP es uno de -eq, -ne, -lt, -le, -gt, o -ge. \layout Standard Las pruebas aritméticas regresan verdadero si \family typewriter ARG1 \family default es igual, distinto, menor que, menor o igual, mayor que, o mayor o igual que \family typewriter ARG2 \family default , respectivamente. \layout Standard Existe otra sintaxis para el operador \family typewriter test \family default . En ves de usar esta palabra clave, se puede encerrar la prueba entre un par \family typewriter [...] \family default . Es importante que ambos parentesis queden aislados, para que el shell los reconozca como unidades independientes. Una prueba de este estilo se puede encadenar con cualquier otra por medio de los operadores de la sección anterior. Por ejemplo, si queremos asegurarnos que un programa existe antes de intentar ejecutarlo, podemos usar la siguiente construcción: \layout LyX-Code [ -f /usr/bin/true -a -x /usr/bin/true ] && /usr/bin/true \layout Standard Que prueba que el archivo existe, es un archivo regular y es ejecutable antes de llamarlo. \layout Subsection Listas \layout Standard La primera manera de controlar el flujo de un script de shell, es mediante la ejecución secuancial, es decir, el efectuar acciones una tras otra. Una lista es una secuencia de \begin_inset Quotes eld \end_inset tubos \begin_inset Quotes erd \end_inset , separados por alguno de los operadores \family typewriter && \family default , \family typewriter || \family default , \family typewriter & \family default , \family typewriter ; \family default y terminados por \family typewriter ; \family default , \family typewriter & \family default o un fin de linea. De entre estos \family typewriter && \family default , y \family typewriter || \family default tienen mayor precedencia, seguidos por \family typewriter & \family default y \family typewriter ; \family default . Los dos primeros tienen el efecto que se describio en la sección anterior. \layout Standard Cuando dos secuencias están separadas por \family typewriter ; \family default el efecto es que se efectuan ambas una tras la otra, sin importar el resultado de las anteriores. El resultado de la lista es el resultado de la última. \layout Standard Cuando una secuencia está terminada por \family typewriter & \family default el shell la ejecuta en el fondo, sin esperar a que termine. El resultado de la lista es 0 (verdadero). \layout LyX-Code ls -R /usr >/tmp/listado; wc -l /tmp/listado \layout Standard Esta secuencia genera un listado de todos los archivos bajo \family typewriter /usr \family default , lo guarda en \family typewriter /tmp/listado \family default y, cuando termina, cuenta cuantas lineas se generaron. \layout Subsection Condicionales \layout Standard Presentamos a continuación las sentencias de control de flujo del shell. Estas nos permiten elegir ejecutar una u otra opción dentro de un script, de acuerdo al resultado de alguna prueba. \layout Subsubsection if \layout Standard La sentencia \family typewriter if \family default nos permite elegir entre dos alterntativas de acuerdo al resultado verdadero o falso de una prueba. Tiene la sintaxis: \layout LyX-Code if \bar under lista \bar default then \bar under lista \bar default [ elif \bar under lista \bar default then \bar under lista \bar default ] ... [ else \bar under lista \bar default ] fi \layout Standard Se ejecuta la primera lista. Si el resultado de esta es verdadero, se ejecuta la segunda. Si no, si hay presente una sección \family typewriter elif \family default se efectua esa prueba, y si es exitosa se efectua la lista asociada. Si ninguna de las secciones \family typewriter if \family default o \family typewriter elif \family default son exitosas, y hay una sección \family typewriter else \family default , se efectuan los comandos dados en esta. Si bien esta sintaxis se presenta en una sola línea, no es necesario escribirla así, y de hecho, al escribir un script es común separarlo en varias lineas, y además indentar el código. \layout LyX-Code if [ ! -f $HOME/.example.conf ] then \begin_deeper \layout LyX-Code echo Ejecutando por primera vez \layout LyX-Code ./configura \end_deeper \layout LyX-Code else \begin_deeper \layout LyX-Code echo Ejecutando por segunda vez \layout LyX-Code ./corre \end_deeper \layout LyX-Code fi \layout Standard Recuerden que tambien se puede poner un comando arbitrario como prueba: \layout LyX-Code if ! grep 'From:.*Juanita' $MAIL; then \begin_deeper \layout LyX-Code echo Juanita no ha escrito \end_deeper \layout LyX-Code else \begin_deeper \layout LyX-Code mail juanita -s 'gracias por tu carta' \end_deeper \layout LyX-Code fi \layout Subsubsection case \layout Standard La sentencia \family typewriter case \family default nos permite elegir una entre varias alternativas, dependiendo del valor de una expresión. La sintaxis es: \layout LyX-Code case \bar under palabra \bar default in [ \bar under patrón \bar default [| \bar under patrón \bar default ]... ) \bar under lista \bar default ;; ] ... esac \layout Standard La palabra es expandida de acuerdo a las reglas usuales (expansión de variables ( \begin_inset LatexCommand \ref{var. ambiente} \end_inset ), comillas inversas ( \begin_inset LatexCommand \ref{comillas-inversas} \end_inset ), etc.) y luego se le compara con cada patrón, en el orden en que aparecen. Cada opción puede tener más de un patrón asociado. El ajuste es el usado para los nombres de archivos. Cuando alguno de los patrones concuerda, se ejecuta la lista asociada, y la busqueda de concordancias se detiene. El resultado total del comando es 0 si ningún patrón ajustó, o el valor de la lista si sí. \layout Standard Si bien la sentencia \family typewriter case \family default no proporciona una acción por defecto, es fácil proporcionar una, poniendo al final de la lista un patrón que ajuste contra cualquier cosa, es decir \family typewriter *) \family default . \layout LyX-Code case $USER in \begin_deeper \layout LyX-Code rodrigo|gunnar) \begin_deeper \layout LyX-Code echo Bienvenidos \layout LyX-Code /usr/local/bin/root-shell;; \end_deeper \layout LyX-Code juan) \begin_deeper \layout LyX-Code echo Tu no eres bienvenido \layout LyX-Code /usr/local/bin/exit;; \end_deeper \layout LyX-Code *) \begin_deeper \layout LyX-Code echo No te conozco;; \end_deeper \end_deeper \layout LyX-Code esac \layout Subsection Ciclos \layout Standard Además de permitir elegir entre varias opciones, el shell nos permite repetir una secuencia de acciones un cierto número de veces, ya sea fijo o determinado por el cumplimiento de una condición. \layout Subsubsection while y until \layout Standard Estas dos sentencias nos permiten efectuar repetidamente una acción, hasta que alguna condición dada se cumpla. La sintaxis es: \layout LyX-Code while \bar under lista \bar default do \bar under lista \bar default done \layout LyX-Code until \bar under lista \bar default do \bar under lista \bar default done \layout Standard \family typewriter while \family default repite la segunda lista siempre que la primera regrese verdadero. \family typewriter until \family default la repite mientras sea falso. El valor de retorno de ambas es el de la lista ejecutada, o cero si no se ejecuto nada. \layout LyX-Code until who | grep -q gunnar; do \begin_deeper \layout LyX-Code echo Gunnar no ha llegado \end_deeper \layout LyX-Code done \layout Subsubsection for \layout Standard La sentencia \family typewriter for \family default nos permite hacer ciclos sobre listas de valores definidos antes de entrar al ciclo. Puesto que el ciclo no se limita a una cantidad de iteraciones, sino que la lista puede ser generada por otro comando, tiene una gran cantidad de usos. La sintaxis es: \layout LyX-Code for \bar under nombre \bar default [ in \bar under palabras; \bar default ] do \bar under lista \bar default ; done \layout Standard La lista de palabras se expande de acuerdo a las reglas usuales. La variable nombre se ajusta a cada valor de la lista resultante en turno, y se efectua la lista para cada valor. Si se omite la parte \family typewriter in \family default , el ciclo se efectua para cada uno de los parametros posicionales ( \begin_inset LatexCommand \ref{pos-param} \end_inset ) del script. \layout Standard El siguiente ciclo entra a cada subdirectorio del directorio actual, y borra todos los archivos *.tmp dentro del mismo. \layout LyX-Code for file in *; do \begin_deeper \layout LyX-Code if [ -d $file ] then \begin_deeper \layout LyX-Code cd $file \layout LyX-Code rm *.tmp \layout LyX-Code cd .. \end_deeper \layout LyX-Code fi \end_deeper \layout LyX-Code done \layout Standard Este otro ciclo imprime un texto cinco veces: \layout LyX-Code for i in 1 2 3 4 5; do \begin_deeper \layout LyX-Code echo texto \end_deeper \layout LyX-Code done \layout Section Funciones \layout Standard Como en todo lenguaje de programación, es conveniente encapsular acciones complejas que se repiten varias veces. Para esto, el shell permite definir funciones, que encierran varias acciones y les dan un nombre por el que pueden ser invocadas. La sintaxis para hacer esto es: \layout LyX-Code [function] \bar under nombre \bar default () { \bar under lista \bar default ; } \layout Standard que asocia al nombre con la lista de comandos entre llaves. Los comandos no se ejecutan en el momento de la definición. Notese que la palabra clave \family typewriter function \family default es opcional, y solo sirve para documentar el script. La funcion es llamada especificando el nombre, como si fuera un comando sencillo. \layout LyX-Code busca_gunnar() { who | grep -q gunnar; } \layout LyX-Code if busca_gunnar then \begin_deeper \layout LyX-Code mail gunnar \end_deeper \layout LyX-Code fi \layout Subsection Parámetros \layout Standard Por supuesto, es deseable que la ejecución de una funcion, o de un script, varien de acuerdo a parametros que se proporcionen al momento de ser ejecutados. Para esto, el shell reserva algunos nombres de variables especiales, cuyos valores son ajustados de forma automática. \layout Subsubsection Parámetros posicionales \begin_inset LatexCommand \label{pos-param} \end_inset \layout Standard El primer tipo de variables de este estilo, son los parametros posicionales. Estos son los parametros que se dan el script o funcion al momento de ser llamados. Se utilizan las variables especiales \family typewriter $ \bar under digitos \family default \bar default , por ejemplo \family typewriter $1 \family default , \family typewriter $2 \family default , etc. Estas variables reciben el valor del parametro pasado en la posición que su número indica. Para referir a los parametros mas allá del \family typewriter $9 \family default , se debe encerrar al número entre llaves: \family typewriter ${10} \family default , de lo contrario, el shell lo interpreta como el parametro \family typewriter $1 \family default , seguido de la cadena ' \family typewriter 0 \family default '. No es posible asignar valores a estos parametros. \layout LyX-Code busca_alguien() { who | grep -q $1; } \layout LyX-Code if busca_alguien gunnar || busca_alguien rodrigo; then \begin_deeper \layout LyX-Code echo Ya llegaron \end_deeper \layout LyX-Code fi \layout Standard O, ejemplificando la sintaxis alterna de \family typewriter for \family default : \layout LyX-Code borra_en_dir() { \begin_deeper \layout LyX-Code for dir; do \begin_deeper \layout LyX-Code if [ -d $dir ] then \begin_deeper \layout LyX-Code cd $dir \layout LyX-Code rm *.tmp \layout LyX-Code cd .. \end_deeper \layout LyX-Code fi \end_deeper \layout LyX-Code done; \end_deeper \layout LyX-Code } \layout LyX-Code borra_en_dir trabajo tmp casa \layout Subsubsection Parámetros especiales --- *, @, #, ?, -, $, !, 0, _ \layout Standard Además de los parametros posicionales, hay otras variables especiales que el shell mantiene. Los valores de estas son asignados automáticamente, y no es posible modificarlo s. Describimos a continuación sus funciones. \layout Description \family typewriter * \family default Expande a la lista de parametros posicionales. Cuando la expansión ocurre dentro de comillas dobles, expande a una sola palabra, que consta de los parametros posicionales separados por el primer caracter de la variable \family typewriter IFS \family default . Si esta variable no tiene un valor, se les separa con espacios. Es util para pasar la lista completa de argumentos recibidos a otro programa. Notese que el otro programa recibirá un solo argumento. \layout Description \family typewriter @ \family default Expande a la lista de parametros posicionales. A diferencia del anterior, cuando la expansión ocurre dentro de comillas dobles, expande a una palabra para cada parametro posicional. En esta forma, se preserva la lista de argumentos tal a como fué dada a este script, y se puede pasar a otro programa tal cual. \layout Description \family typewriter # \family default Expande al número de argumentos posicionales. \layout Description \family typewriter ? \family default Expande al código de salida del último comando ejecutado. \layout Description \family typewriter - \family default Expande a la lista de opciones del shell activadas, ya sea al ser invocada, o por el comando interno \family typewriter set \family default . \layout Description \family typewriter $ \family default Expande al identificador de proceso del shell. \layout Description \family typewriter ! \family default Expande al identificador de proceso del último comando ejecutado en el fondo. \layout Description \family typewriter 0 \family default Expande al nombre del shell, o del script. \layout Description \family typewriter _ \family default Expande al último argumento del último comando ejecutado. \layout Section Asuntos esotéricos en el shell \layout Standard Describimos a continuación algunas de las caracteristicas más \begin_inset Quotes eld \end_inset esotericas \begin_inset Quotes erd \end_inset del shell. Si bien estas no son necesarias para el trabajo diario con el mismo, resulta útil conocerlas, puesto que permiten realizar cosas que son difíciles o imposibles de otra manera. \layout Subsection Here Documents (<< \emph on algo \emph default ) \layout Standard En ocasiones queremos ejecutar un comando con una entrada fija, pero no queremos poner ésta en un archivo desde el cual redireccionar la entrada. Ponerla en un archivo nos obliga a mantenerla en una ruta fija, confiendo en que nadie lo borre o modifique por no saber para que se usa (incluso nosotros mismos). Para evitar eso, podemos poner esta entrada en el texto mismo del script, y pedir al shell que alimente al comando con esta entrada. Para eso, decimos \layout LyX-Code \bar under comando \bar default << \bar under algo \layout LyX-Code Aqui ponemos el texto \layout LyX-Code tantas lineas como queramos \layout LyX-Code \bar under algo \layout Standard El texto comienza en la linea siguiente al comando, y está delimitado por una linea que contenga solamente la palabra que pusimos despues del \family typewriter << \family default . Al ejecutarse, el shell alimenta al comando con el texto, y luego se salta el texto y continua la ejecución en la linea siguiente. Es posible alimentar a una tubería de este modo, por ejemplo, sabiendo que el programa \family typewriter fmt \family default formatea su entrada a parrafos con lineas de una longitud dada, por defecto 75 caracteres, podemos enviar un correo sin preocuparnos de escribirlo en lineas cortas: \layout LyX-Code fmt <$file.new \layout LyX-Code mv $file.new $file \end_deeper \layout LyX-Code done \layout Standard Si bien esta caracteristica es poderosa, puede llevar facilmente a una sintaxis enmarañada, con multiples niveles de anidamiento de comillas que es necesario proteger de la expansión. Podemos evitar esto en la mayoría de los casos, usando el comando \family typewriter xargs \family default . Este comando toma su entrada estandar, la separa en palabras, y ejecuta al comando dado como argumento con estas palabras como argumentos extra. Si la linea de argumentos resultaría demasiado larga, \family typewriter xargs \family default la parte en varias, y ejecuta al comando varias veces. Por ejemplo, para averiguar en que archivo .h se define a cierta estructura, podemos decir: \layout LyX-Code find . -name '*.h' | xargs grep 'struct *cierta_estructura' \layout Subsubsection Redirecciones complejas \layout Standard El shell nos permite efectuar algunas redirecciones más complejas que las mostradas al inicio. Por ejemplo, es posible redirigir descriptores de archivo especificos. Si decimos \family typewriter n> \bar under archivo \family default \bar default , el archivo será abierto para escritura, en el descriptor de archivo n. Analogamente, es posible hacer esto para lectura, o para escribir al final. Por supuesto, cualquier descriptor fuera de 0, 1 y 2 es no estandar, de modo que el programa que ejecutemos debe estar esperando que dichos descriptore s esten abiertos y usarlos, o no pasará nada. \layout Standard Pero de cualquier forma es útil esta caracteristica. Supongamos que queremos desacernos de la salida estandar, y guardar los mensajes de error en un archivo. Entonces podemos decir \family typewriter comando >/dev/null 2>/tmp/log \family default , lo cual logra nuestro proposito. Comunmente, queremos procesar la salida de error, junto con la normal. Para esto, podemos 'duplicar' un descriptor de archivo, diciendo, por ejemplo \family typewriter comando 2>&1 \family default , que manda la salida de error a donde va la estandar. Si queremos procesar la salida de error en una tubería, y deshacernos de la salida normal, primero duplicamos la normal sobre la de error, y luego redirigimos la normal a \family typewriter /dev/null \family default : \family typewriter comando 2>&1 >/dev/null \family default . Notese que esto funciona por que la primera redirección copia el descriptor uno sobre el dos, no lo liga. Además, el orden importa. Si decimos \family typewriter comando >/dev/null 2>&1 \family default primero se redirige la salida a \family typewriter /dev/null \family default , y luego esa salida redirigida se copia sobre la salida de error, de modo que ambas se van al mismo lugar. Esta última operación es tan comun, que hay una sintaxis especial para ella: \family typewriter comando &> \bar under archivo \family default \bar default redirige tanto la salida estandar como la salida de error al archivo nombrado. \layout Subsection Agrupación de procesos \layout Standard En ocasiones queremos ejecutar un proceso complejo o tardado, con un \family typewriter for \family default o un \family typewriter while \family default , por ejemplo, y queremos dejarlo trabajando en el fondo como una unidad. Para hacer esto, podemos encerrar el comando completo entre parentesis. Entonces, el shell tratará al comando como una unidad, suspendiendolo o reanudandolo junto. Esto resulta también util cuando el proceso a ejecutarse cambia el entorno del shell. Como el proceso es ejecutado por un subshell, cualquier modificación al entorno solo afecta al del subshell, y no al original. \layout Subsection Substitución de procesos \layout Standard La sustitución de procesos es una caracteristica que no está disponible en todos los sistemas. Es necesario que el sistema soporte los 'named pipes' o el sistema de archivos \family typewriter /dev/fd \family default . Si existe el soporte, bash puede efectuar una sustitución del estilo \family typewriter <( \bar under lista \bar default ) \family default o \family typewriter >( \bar under lista \bar default ) \family default . La lista de procesos se efectua con su salida o su entrada, respectivamente, conectada a un 'named pipe', o a un archivo en \family typewriter /dev/fd \family default . El nombre de este archivo se sustituye en vez de la lista, como un argumento al comando. El comando puede abrir el archivo y usarlo para entrada o salida, segun el caso. \layout Standard Si bien no es fácil ver el uso de esta caracteristica, puede resultar muy poderosa en algunos casos. Por ejemplo, el programa \family typewriter diff \family default compara el contenido de dos archivos. Usando este metodo, podemos usarlo para comparar la salida de dos comandos distintos, sin necesidad de grabar la salida de estos a archivos. Uniendo esto con un programa como \family typewriter lynx \family default , podemos por ejemplo comparar dos versiones de una página web, guardadas en distintos servidores, sin tener que guardarlas en ningun lado: \layout LyX-Code diff -u <(lynx -source \bar under url1 \bar default ) <(lynx -source \bar under url2 \bar default ) \layout Standard \layout Bibliography \bibitem {man bash} Página de manual de bash(1) \the_end