<!doctype linuxdoc system>

<!-- 
     Una introducción a la programación de la shell BASH
 -->

<!-- Información titular -->

<article>
    <title>Programación en BASH - COMO de introducción</title>
    <author>Mike G <tt>(mikkey) disponible en dynamo.com.ar</tt>&nl;
    Traducido por Gabriel Rodríguez Alberich <tt><htmlurl
    name="chewie@asef.us.es" url="mailto:chewie@asef.us.es"></tt>&nl;
   </author>
    <date> 
	jueves, 27 de julio de 2000, a las 09:36:18 ART
    </date>
    <abstract> 
    Este artículo pretende ayudarle a comenzar a programar shell
    scripts a un nivel básico/intermedio. No pretende ser un documento
    avanzado (vea el título). NO soy un experto ni un gurú de la
    programación en shell. Decidí escribir esto porque aprenderé mucho
    con ello y puede serle útil a otras personas. Cualquier aportación
    será apreciada, especialmente en forma de parche :)
    </abstract>

<!-- Table of contents -->
<toc>

<!-- Requisites -->

<sect>
Introducción


<sect1>
Obteniendo la última versión

<p>
<htmlurl url="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html"
     name="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html">


     </sect1>

     <sect1>
     Requisitos
          <P> Le será útil tener una cierta familiaridad con la línea de 
	    comandos de GNU/Linux y con los conceptos básicos de la programación.       
          Aunque esto no es una introducción a la programación, explica   
          (o al menos lo intenta) muchos conceptos básicos.
          <P> 
     </sect1>
     <sect1>
     Usos de este documento
          <P> Este documento intenta ser útil en las siguientes 
situaciones
	  <itemize>
	      <item> Si tiene alguna idea de programación y quiere empezar a
              programar algunos shell scripts.
	      <item> Si tiene una idea vaga de programar en shell y quiere algún
              tipo de referencia.
	      <item> Si quiere ver algunos scripts y comentarios para empezar a
              escribir los suyos propios.
	      <item> Si está migrando desde DOS/Windows (o ya lo ha hecho) y quiere
              hacer procesos "por lotes".
	      <item> Si es un completo novato y lee todo COMO disponible.
	  </itemize>
     </sect1>
</sect>

<!-- Scripts muy sencillos --> 
<sect>
Scripts muy sencillos

    <P> Este COMO tratará de darle algunos consejos sobre la programación
    de shell scripts, basándose profundamente en ejemplos.
    <P> En esta sección encontrará varios scripts pequeños que
    esperanzadamente le ayudarán a entender algunas técnicas.

    <sect1>
    Típico script `hola mundo'

        <!-- __TO-DO__
	someone suggested using this instead of the line numbering 
	but it doesn't work on version 1.0.9
	<pre></pre> 
	-->
	<P>
	<tscreen><verb>
	  #!/bin/bash          
	  echo Hola Mundo 
	</verb></tscreen>
	<P> 
	<P> Este script tiene sólo dos líneas.
        La primera le indica al sistema qué programa 
        usar para ejecutar el fichero.
        <P> La segunda línea es la única acción realizada por este
        script, que imprime 'Hola Mundo' en la terminal.
	<P> Si le sale algo como <it>./hello.sh: Comando desconocido.</it>,
        probablemente la primera línea, '#!/bin/bash', está mal. Ejecute
        <tt>whereis bash</tt>, o vea 'encontrando el bash' para saber cómo
	debe escribir esta línea.
    </sect1>
    <sect1>
    Un script de copia de seguridad muy simple
        <P>
	<tscreen><verb>
	#!/bin/bash          
	tar -cZf /var/my-backup.tgz /home/yo/
	</verb></tscreen>
        <P> En este script, en vez de imprimir un mensaje en la terminal,
        creamos un tar-ball del directorio home de un usuario. Esto NO
        pretende ser un script útil; más tarde se ofrece un script de
        copia de seguridad más útil.
    </sect1>   
</sect>

<!-- Redirección -->
<sect>
Todo sobre redirección

	<sect1>
	Teoría y referencia rápida
	<p> Existen 3 descriptores de ficheros: stdin, stdout y stderr 
(std=estándar).

	<p>Básicamente, usted puede:
	<enum>
	<item> redirigir stdout a un fichero
	<item> redirigir stderr a un fichero
	<item> redirigir stdout a stderr 
	<item> redirigir stderr a stdout 
	<item> redirigir stderr y stdout a un fichero
	<item> redirigir stderr y stdout a stdout 
	<item> redirigir stderr y stdout a stderr
	</enum>
	El número 1 'representa' a stdout, y 2 a stderr.
	<p> Una pequeña nota para ver todo esto: con el comando less puede
        visualizar stdout (que permanecerá en el búfer) y stderr, que se
        imprimirá en la pantalla, pero será borrado si intenta leer el
        búfer.
	</sect1>
	
	<sect1>
	Ejemplo: stdout a un fichero
	<p> Esto hará que la salida de un programa se escriba en un fichero.
	<tscreen><verb>
	ls -l &gt; ls-l.txt
	</verb></tscreen>
	En este caso, se creará un fichero llamado 'ls-l.txt' que contendrá lo
	que se vería en la pantalla si escribiese el comando 'ls -l' y lo
	ejecutase.
	</sect1>

	<sect1>
	Ejemplo: stderr a un fichero
	<p> Esto hará que la salida stderr de un programa se escriba en un
	fichero.
	<tscreen><verb>
	grep da * 2&gt; errores-de-grep.txt
	</verb></tscreen>
	En este caso, se creará un fichero llamado 'errores-de-grep.txt' que
	contendrá la parte stderr de la salida que daría el comando 'grep da *'.
	</sect1>

	<sect1>
	Ejemplo: stdout a stderr
	<p> Esto hará que la salida stdout de un programa se escriba en el mismo 
	descriptor de fichero que stderr.
	<tscreen><verb>
	grep da * 1&gt;&amp;2 
	</verb></tscreen>
	En este caso, la parte stdout del comando se envía a stderr; puede
	observar eso de varias maneras.
	</sect1>

	<sect1>
	Ejemplo: stderr a stdout
	<p> Esto hará que la salida stderr de un programa se escriba en el mismo
	descriptor de fichero que stdout.
	<tscreen><verb>
	grep * 2&gt;&amp;1
	</verb></tscreen>
	En este caso, la parte stderr del comando se envía a stdout. Si hace una 
	tubería con less, verá que las líneas que normalmente 'desaparecen' (al
	ser escritas en stderr), ahora permanecen (porque están en el stdout).
	</sect1>

	<sect1> Ejemplo: stderr y stdout a un fichero
	<p>
	Esto colocará toda la salida de un programa en un fichero. A veces,
	esto es conveniente en las entradas del cron, si quiere que un comando se
	ejecute en absoluto silencio.
	<tscreen><verb>
	rm -f $(find / -name core) &amp;&gt; /dev/null 
	</verb></tscreen>
	Esto (pensando en la entrada del cron) eliminará todo archivo llamado 
`core' en cualquier directorio. Tenga en cuenta que tiene que estar muy 
seguro de lo que hace un comando si le va a eliminar la salida.
	</sect1>
<!--
redirigir stderr y stdout a stdout 
redirigir stderr y stdout a stderr
-->



<!--
	<sect1>
	Ejemplo: 
	<p> Esto
	<tscreen><verb>
	command
	</verb></tscreen>
	Explicación
	</sect1>
-->

</sect>

<!-- tuberías -->
<sect>
Tuberías
	<p> Esta sección explica de una manera muy sencilla y práctica cómo
	utilizar tuberías, y por qué querría utilizarlas.

	<sect1>
	Qué son y por qué querrá utilizarlas
		<p> Las tuberías le permiten utilizar (muy sencillo, insisto) la salida 
		de un programa como la entrada de otro.
	</sect1>


	<sect1>
	Ejemplo: una tubería sencilla con sed
	<p> Ésta es una manera muy sencilla de utilizar tuberías.
	<tscreen><verb>
	ls -l | sed -e "s/[aeio]/u/g"	
	</verb></tscreen>
	En este caso, ocurre lo siguiente: primero se ejecuta el comando ls -l,
	y luego su salida, en vez de imprimirse en la pantalla, se envía
	(entuba) al programa sed, que imprime su salida correspondiente.
	</sect1>
	<sect1>
	Ejemplo: una alternativa a ls -l *.txt 
	<p> Probablemente ésta es una manera más difícil de hacer un ls -l
	*.txt, pero se muestra para ilustrar el funcionamiento de las tuberías,
	no para resolver ese dilema.
	<tscreen><verb>
	ls -l | grep "\.txt$"
	</verb></tscreen>
	En este caso, la salida del programa ls -l se envía al programa grep,
	que imprimirá las líneas que concuerden con la regex
	(expresión regular) "\.txt$".
	</sect1>


<!--
	<sect1>
	Ejemplo: 
	<p> Esto
	<tscreen><verb>
	command
	</verb></tscreen>
	Explicación
	</sect1>
-->


</sect>




<!-- Variables -->
<sect>
Variables

    <!-- Pura teoría -->
        <P> Puede usar variables como en cualquier otro lenguaje de
	programación. No existen tipos de datos. Una variable de bash puede
	contener un número, un caracter o una cadena de caracteres.
	<P> No necesita declarar una variable. Se creará sólo con asignarle un
	valor a su referencia.

	

    <!-- Ejemplos -->
        <sect1>
	Ejemplo: ¡Hola Mundo! utilizando variables
            <P> 
	    <tscreen><verb>
	    #!/bin/bash          
	    CAD="¡Hola Mundo!"
	    echo $CAD    
	    </verb></tscreen>

	    <P> La segunda línea crea una variable llamada STR y le asigna la
	cadena "¡Hola Mundo!". Luego se recupera el VALOR de esta variable
	poniéndole un '$' al principio. Por favor, tenga en cuenta (¡inténtelo!)
	que si no usa el signo '$', la salida del programa será diferente, y
	probablemente no sea lo que usted quería.
	</sect1>

        <sect1>
	   Ejemplo: Un script de copia de seguridad muy simple (algo mejor)
	   <P> 
	   <tscreen><verb>
	   #!/bin/bash          
	   OF=/var/mi-backup-$(date +%Y%m%d).tgz
	   tar -cZf $OF /home/yo/
	   </verb></tscreen>
	   <P> Este script introduce algo nuevo. Antes que nada, debería
	familiarizarse con la creación y asignación de variable de la línea 2.
	Fíjese en la expresión '$(date +%Y%m%d)'. Si ejecuta el script se dará
	cuenta de que ejecuta el comando que hay dentro de los paréntesis,
	capturando su salida.

	   <P> Tenga en cuenta que en este script, el fichero de salida será
	distinto cada día, debido al formato pasado al comando date (+%Y%m%d).
	Puede cambiar esto especificando un formato diferente.
	   <P> Algunos ejemplos más:
	   <P> echo ls
	   <P> echo $(ls)
	</sect1>  

	<sect1>
	Variables locales

	<p> Las variables locales pueden crearse utilizando la palabra clave
	<it/local/.

	<tscreen><verb>
		#!/bin/bash
		HOLA=Hola
		function hola {
			local HOLA=Mundo
			echo $HOLA
		}
		echo $HOLA
		hola
		echo $HOLA
	</verb></tscreen>
	<p> Este ejemplo debería bastar para mostrarle el uso de una variable
	local.
	</sect1>
 
</sect>

<!-- Estructuras Condicionales -->
<sect>
Estructuras Condicionales
    <P> Las estructuras condicionales le permiten decidir si se realiza una
    acción o no; esta decisión se toma evaluando una expresión.

    <!-- Pura teoría -->
    <sect1>
    Pura teoría
        <P> Los condicionales tienen muchas formas. La más básica es:
	<bf>if</bf> <it/expresión/ <bf>then</bf> <it/sentencia/
	donde 'sentencia' sólo se ejecuta si 'expresión' se evalúa como
	verdadera. '2&lt;1' es una expresión que se evalúa falsa, mientras
	que '2&gt;1' se evalúa verdadera.
	<p> Los condicionales tienen otras formas, como:
	<bf/if/ <it/expresión/ 
	<bf/then/ <it/sentencia1/ <bf/else/ <it/sentencia2/.
	Aquí 'sentencia1' se ejecuta si 'expresión' es verdadera. De otra
	manera se ejecuta 'sentencia2'.
	<P> Otra forma más de condicional es:
	<bf/if/ <it/expresión1/ 
	<bf/then/ <it/sentencia1/ 
	<bf/else if/ <it/expresión2/ <bf/then/ <it/sentencia2/ 
	<bf/else/ <it/sentencia3/.
	En esta forma sólo se añade "ELSE IF 'expresión2' THEN 'sentencia2'",
	que hace que sentencia2 se ejecute si expresión2 se evalúa verdadera.
	El resto es como puede imaginarse (véanse las formas anteriores).
	<P> Unas palabras sobre la sintaxis:
	<P> La base de las construcciones 'if' es ésta:
	<P> if [expresión];
	<P> then
	<P>    código si 'expresión' es verdadera.
	<P> fi
    </sect1>
    <!-- Ejemplos -->
        <sect1>
	Ejemplo: Ejemplo básico de condicional if .. then
            <P> 
	    <tscreen><verb>
	    #!/bin/bash
	    if [ "petete" = "petete" ]; then
	       echo expresión evaluada como verdadera
	    fi
	    </verb></tscreen>
	    <P> El código que se ejecutará si la expresión entre corchetes es
	verdadera se encuentra entre la palabra 'then' y la palabra 'fi', que
	indica el final del código ejecutado condicionalmente.
	</sect1>
	<sect1>
	Ejemplo: Ejemplo básico de condicional if .. then ... else
	    <P>
	    <tscreen><verb>
	    #!/bin/bash	    if [ "petete" = "petete" ]; then
	       echo expresión evaluada como verdadera
	    else
	       echo expresión evaluada como falsa
	    fi
	    </verb></tscreen>
	</sect1>
	<sect1>
	Ejemplo: Condicionales con variables
	    <P> 
	    <tscreen><verb>
	    #!/bin/bash
	    T1="petete"
	    T2="peteto"
	    if [ "$T1" = "$T2" ]; then
	        echo expresión evaluada como verdadera
	    else
	        echo expresión evaluada como falsa
	    fi
	    </verb></tscreen>
	</sect1>

	<sect1>
	Ejemplo: comprobando si existe un fichero
	    <P> un agradecimiento más a mike
	    <tscreen><verb>
	    #!/bin/bash
	    FILE=~/.basrc
	    if [ -f $FILE ]; then
	        echo el fichero $FILE existe
	    else
	        echo fichero no encontrado
	    fi
	    if [ 'test -f $FILE']
	    </verb></tscreen>
	</sect1>


</sect>

<!-- Bucles: for, while y until -->
<sect>
Los bucles for, while y until
    <!-- Pura teoría -->

    <P> En esta sección se encontrará con los bucles for, while y until.
    <P> El bucle <bf/for/ es distinto a los de otros lenguajes de
	programación. Básicamente, le permite iterar sobre una serie de
	`palabras' contenidas dentro de una cadena.
      <P> El bucle <bf/while/ ejecuta un trozo de códico si la expresión
	de control es verdadera, y sólo se para cuando es falsa (o se encuentra
	una interrupción explícita dentro del código en ejecución).

      <P> El bucle <bf/until/ es casi idéntico al bucle loop, excepto en
	que el código se ejecuta mientras la expresión de control se evalúe como
	falsa.
      <P> Si sospecha que while y until son demasiado parecidos, está en
	lo cierto.
    <!-- Ejemplos -->    
    <sect1>
    Por ejemplo
        <P> 
	<tscreen><verb>
        #!/bin/bash
        for i in $( ls ); do
            echo item: $i
        done
        </verb></tscreen>
        <p> 
        <p> En la segunda línea declaramos i como la variable que
	recibirá los diferentes valores contenidos en $( ls ).
        <p> La tercera línea podría ser más larga o podría haber más
	líneas antes del done (4).
        <p> `done' (4) indica que el código que ha utilizado el valor de
	$i ha acabado e $i puede tomar el nuevo valor.
        <p> Este script no tiene mucho sentido, pero una manera más útil
	de usar el bucle for sería hacer que concordasen sólo ciertos
	ficheros en el ejemplo anterior.
    <p>
    </sect1>
	<sect1>
	for tipo-C
	<P> Fiesh sugirió añadir esta forma de bucle. Es un bucle for más
	parecido al for de C/perl...
	<tscreen><verb>
	#!/bin/bash
	for i in `seq 1 10`;
	do
		echo $i
	done	
	</verb></tscreen>
	</sect1>
    <!-- while -->
    <sect1>
    Ejemplo de while
         <P> <tscreen><verb>
         #!/bin/bash 
         CONTADOR=0
         while [  $CONTADOR -lt 10 ]; do
             echo El contador es $CONTADOR
             let CONTADOR=CONTADOR+1 
         done
         </verb></tscreen>
	 <P>
	 <P> Este script 'emula' la conocida (C, Pascal, perl, etc) estructura
	`for'.
    </sect1>
    <!-- until -->
    <sect1>
    Ejemplo de until
         <P> <tscreen><verb>
         #!/bin/bash 
         CONTADOR=20
         until [  $CONTADOR -lt 10 ]; do
             echo CONTADOR $CONTADOR
             let CONTADOR-=1
         done
	 </verb></tscreen>
    </sect1>
</sect>

<!-- Funciones -->
<sect>
Funciones
     <P> Como en casi todo lenguaje de programación, puede utilizar
	funciones para agrupar trozos de código de una manera más lógica, o
	practicar el divino arte de la recursión.
     <P> Declarar una función es sólo cuestión de escribir function
	mi_func { mi_código }.
     <P> Llamar a la función es como llamar a otro programa, sólo hay que
	escribir su nombre.
     <P>
     <sect1>
     Ejemplo de funciones
           <P> <tscreen><verb>
	   #!/bin/bash 
	   function salir {
	       exit
	   }
	   function hola {
	       echo ¡Hola!
	   }
	   hola
	   salir
	   echo petete 
	   </verb></tscreen>
	   <P> Las líneas 2-4 contienen la función 'salir'. Las líneas 5-7
	contienen la función 'hola'. Si no está completamente seguro de lo que
	hace este script, por favor, ¡pruébelo!.
	   <P> Tenga en cuenta que una función no necesita que sea declarada en
	un orden específico.
	   <P> Cuando ejecute el script se dará cuenta de que: primero se llama
	a la función 'hola', luego a la función 'quit', y el programa nunca llega
	a la línea 10.
     </sect1>

     <sect1>
     Ejemplo de funciones con parámetros
           <P> <tscreen><verb>
		#!/bin/bash 
		function salir {
 		   exit
		}  
		function e {
		    echo $1 
		}  
		e Hola
		e Mundo
		salir
		echo petete 

	   </verb></tscreen>
	   <P> Este script es casi idéntico al anterior. La diferencia principal
	es la función 'e'. Esta función imprime el primer argumento que recibe.
	Los argumentos, dentro de las funciones, son tratados de la misma manera
	que los argumentos suministrados al script.
     </sect1>


</sect>


<!-- Interfaces de usuario -->
<sect>
Interfaces de usuario
     <!-- Menús sencillos con select -->
     <sect1>
     Utilizando select para hacer menús sencillos
	   <P>
	   <tscreen><verb>
	   #!/bin/bash
	   OPCIONES="Hola Salir"
	   select opt in $OPCIONES; do
	       if [ "$opt" = "Salir" ]; then
	   	echo done
	   	exit
	       elif [ "$opt" = "Hola" ]; then
	   	echo Hola Mundo
	       else
	   	clear
	   	echo opción errónea
	       fi
	   done
	  </verb></tscreen> 
           <P> Si ejecuta este script verá que es el sueño de un 
programador para hacer menús basados en texto. Probablemente se dará 
cuenta de que es muy similar a la construcción 'for', sólo que en vez de 
iterar para cada 'palabra' en $OPCIONES, se lo pide al usuario.
	   <P>
     </sect1>
    <!-- Utilizando la línea de comandos -->
    <sect1>
    Utilizando la línea de comandos
         <P>
	 <tscreen><verb>
	  #!/bin/bash        
          if [ -z "$1" ]; then 
              echo uso: $0 directorio
              exit
          fi
          SRCD=$1
          TGTD="/var/backups/"
          OF=home-$(date +%Y%m%d).tgz
          tar -cZf $TGTD$OF $SRCD
	 </verb></tscreen>
	 <P>
	 <P> Lo que hace este script debería estar claro para usted. La 
expresión del primer condicional comprueba si el programa ha recibido 
algún argumento ($1) y sale si no lo ha recibido, mostrándole al usuario 
un pequeño mensaje de uso. El resto del script debería estar claro.
    </sect1>

</sect>

<!-- Miscelánea -->
<sect>
Miscelánea
    <!-- Leyendo información del usuario  -->    
    <sect1>
    Leyendo información del usuario
         <P> En muchas ocasiones, puede querer solicitar al usuario
	alguna información, y existen varias maneras para hacer esto. Ésta es
	una de ellas:
	<tscreen><verb>
		#!/bin/bash
		echo Por favor, introduzca su nombre
		read NOMBRE
		echo "¡Hola $NOMBRE!"
	</verb></tscreen>
	<P> Como variante, se pueden obtener múltiples valores con read. Este
	ejemplo debería clarificarlo.
	<tscreen><verb>
		#!/bin/bash
		echo Por favor, introduzca su nombre y primer apellido
		read NO AP 
		echo "¡Hola $AP, $NO!"
	</verb></tscreen>
	
    </sect1>

    <!-- Evaluación aritmética  -->    
    <sect1>
    Evaluación aritmética
         <P> Pruebe esto en la línea de comandos (o en una shell):
	 <P> echo 1 + 1
	 <P> Si esperaba ver '2', quedará desilusionado. ¿Qué hacer si quiere
	que BASH evalúe unos números? La solución es ésta:
	 <P> echo $((1+1))
	 <P> Esto producirá una salida más 'lógica'. Esto se hace para evaluar
	una expresión aritmética. También puede hacerlo de esta manera:
	 <P> echo $[1+1]
	 <P>
	 <P> Si necesita usar fracciones, u otras matemáticas, puede utilizar bc
	para evaluar expresiones aritméticas.
	 <P> Si ejecuta "echo $[3/4]" en la línea de comandos, devolverá 0,
	porque bash sólo utiliza enteros en sus respuestas. Si ejecuta "echo
	3/4|bc -l", devolverá 0.75.
    </sect1>


    <sect1>
    Encontrando el bash
        <P> De un mensaje de mike (vea los agradecimientos):
        <P> siempre usas #!/bin/bash .. a lo mejor quieres dar un ejemplo
        <P> de cómo saber dónde encontrar el bash.
        <P> `locate bash' es preferible, pero no todas las máquinas
	<P> tienen locate.
        <P> `find ./ -name bash' desde el directorio raíz funcionará,
	<P> normalmente.
        <P> Sitios donde poder buscar:
        <P>         ls -l /bin/bash
        <P>         ls -l /sbin/bash
        <P>         ls -l /usr/local/bin/bash
        <P>         ls -l /usr/bin/bash
        <P>         ls -l /usr/sbin/bash
        <P>         ls -l /usr/local/sbin/bash
        <P> (no se me ocurre ningún otro directorio...  lo he encontrado
        <P> la mayoría de estos sitios en sistemas diferentes).
	<P> También puedes probar 'which bash'.
   </sect1>

    <!-- Capturando la salida de un comando  -->    
    <sect1>
    Obteniendo el valor devuelto por un programa
        <P> En bash, el valor de retorno de un programa se guarda en una
	variable especial llamada $?.
	<P> Esto ilustra cómo capturar el valor de retorno de un programa. Supongo
	que el directorio <it/dada/ no existe. (Esto también es sugerencia de
	Mike).
	<tscreen><verb>
	#!/bin/bash
	cd /dada &amp;&gt; /dev/null
	echo rv: $?
	cd $(pwd) &amp;&gt; /dev/null
	echo rv: $?
	</verb> </tscreen>
    </sect1>
 
 
    <!-- Capturando la salida de un comando  -->    
    <sect1>
    Capurando la salida de un comando
         <P> Este pequeño script muestra todas las tablas de todas las
	bases de datos (suponiendo que tenga MySQL instalado).
	Considere también cambiar el comando 'mysql' para que use un nombre de
	usuario y clave válidos.
	<tscreen><verb>
	#!/bin/bash
	DBS=`mysql -uroot  -e"show databases"`
	for b in $DBS ;
	do
	        mysql -uroot -e"show tables from $b"
	done
	</verb></tscreen>
    </sect1>
    
    <!-- Multiple source files
    <sect1>
    Multiple source files
        <P> You can use multiple files with the command source. 
	<P> __TO-DO__
    </sect1>
   -->
</sect>


<!-- Tablas -->
<sect>
Tablas
    <!-- Operadores de cadena -->
    <sect1>
    Operadores de comparación de cadenas

    <P>
    <descrip>
    <tag>s1 = s2</tag> s1 coincide con s2

    <tag>s1 != s2</tag> s1 no coincide con s2

    <tag>s1 < s2</tag> s1 es alfabéticamente anterior a s2, con el 
<it>locale</it> actual

    <tag>s1 > s2</tag> s1 es alfabéticamente posterior a s2, con el 
<it>locale</it> actual

    <tag>-n s1</tag> s1 no es nulo (contiene uno o más caracteres)

    <tag>-z s1</tag> s1 es nulo
   </descrip>
    </sect1>

	<sect1>
	Ejemplo de comparación de cadenas
	<p> Comparando dos cadenas
	<tscreen><verb>
	#!/bin/bash
	S1='cadena'
	S2='Cadena'
	if [ $S1!=$S2 ];
	then
	        echo "S1('$S1') no es igual a S2('$S2')"
	fi
	if [ $S1=$S1 ];
	then
	        echo "S1('$S1') es igual a S1('$S1')"
	fi
	</verb></tscreen>
	<p> Cito aquí el consejo de un correo enviado por Andreas Beck, referido
	al uso de <it/if [ $1 = $2 ]/.
	<p>  Esto no es buena idea, porque si $S1 o $S2 son vacíos, aparecerá un
	<it>parse error</it>. Es mejor: x$1=x$2 or "$1"="$2"

	</sect1>

    <!-- Operadores aritméticos -->    
    <sect1>
    Operadores aritméticos
    <P> + (adición)
    <P> - (sustracción)
    <P> * (producto)
    <P> / (división)
    <P> % (módulo)
    </sect1>

    <!-- Operadores relacionales aritméticos -->    
    <sect1>
    Operadores relacionales aritméticos
    <P> -lt (<) 
    <P> -gt (>)
    <P> -le (<=)
    <P> -ge (>=)
    <P> -eq (==)
    <P> -ne (!=)
    <P> Los programadores de C tan sólo tienen que corresponder el
	operador con su paréntesis.
    </sect1>
    

    <!-- Comandos útiles -->    
    <sect1>
    Comandos útiles
         <P> Esta sección ha sido reescrita por Kees (véanse agradecimientos)
	 <P> Algunos de estos comandos contienen lenguajes de programación
	completos. Sólo se explicarán las bases de estos comandos. Para una
	descripción más detallada, eche un vistazo a las páginas man de cada uno.

	<bf/sed/ (editor de flujo)

   	<P> Sed es un editor no interactivo. En vez de alterar un fichero 
moviendo el cursor por la pantalla, se utiliza una serie de instrucciones 
de edición de sed, y el nombre del fichero a editar. También se puede 
describir a sed como un filtro. Miremos algunos ejemplos:

   	<tscreen><verb>
	$sed 's/a_sustituir/sustituto/g' /tmp/petete
	</verb></tscreen>

   	<P> Sed sustituye la cadena 'a_sustituir' por la cadena 'sustituto', 
leyendo del fichero /tmp/petete. El resultado se envía a stdout 
(normalmente la consola), pero se puede añadir '> captura' al final de la 
línea de arriba para que sed envíe la salida al fichero 'capture'.

	<tscreen><verb>
   	$sed 12, 18d /tmp/petete
	</verb></tscreen>	

   	<P> Sed muestra todas las líneas de /tmp/petete excepto la 12 y la 
18. El fichero original no queda alterado por este comando.

	<bf/awk/ (manipulación de bases de datos, extracción y proceso de texto)

   	<P> Existen muchas implementaciones del lenguaje de programacin AWK 
(los intérpretes más conocidos son gawk de GNU, y el 'nuevo awk' mawk). 
El principio es sencillo: AWK busca un patrón, y por cada patrón de 
búsqueda que coincida, se realiza una acción.
   	<P> Si tenemos un fichero /tmp/petete con las siguientes líneas:
	<P> <it/"prueba123/
   	<P> <it/prueba/
	<P> <it/pprruueebbaa"/

	<P> y ejecutamos:
   	<tscreen><verb>
	$awk '/prueba/ {print}' /tmp/petete
	</verb></tscreen>
   	<P> test123

   	<P> test

   	<P> El patrón que busca AWK es 'prueba' y la acción que realiza
	cuando encuentra una línea en /tmp/petete con la cadena 'prueba' es
	`print'.

   	<tscreen><verb>
	$awk '/prueba/ {i=i+1} END {print i}' /tmp/petete
	</verb></tscreen>

   	<P> 3

   	<P> Cuando se utilizan muchos patrones, se puede reemplazar el texto
	entre comillas por '-f fichero.awk', y poner todos los patrones y
	acciones en 'fichero.awk'.

   	<bf/grep/ (impresión de líneas que coinciden con un patrón de
	búsqueda)

   <P> Ya hemos visto ejemplos del comando grep en los capítulos
	anteriores, que muestra las líneas que concuerdan con un patrón. Pero
	grep puede hacer más que eso.
   	<tscreen><verb>
   	$grep "busca esto" /var/log/messages -c
	</verb></tscreen>
   <P> 12
   <P> Se ha encontrado 12 veces la cadena "busca esto" en el fichero
	/var/log/messages.

   <P> [vale, este ejemplo es falso, el fichero /var/log/messages está
	alterado :-)]

   <bf/wc/ (cuenta líneas, palabras y bytes)

   <P> En el siguiente ejemplo, vemos que la salida no es lo que 
esperábamos. El fichero petete utilizado en este ejemplo contiene el 
texto siguiente:

       	<it/"programación en bash/&nl;
     	<it/como de introducción"/

   <tscreen><verb>
	$wc --words --lines --bytes /tmp/petete
	</verb></tscreen>

   <P> 2 5 41 /tmp/petete

   <P> Wc no tiene en cuenta el orden de los parámetros. Wc siempre los 
imprime en un orden estándar, que es, como se puede ver: líneas, 
palabras, bytes y fichero.

   <bf/sort/ (ordena líneas de ficheros de texto)

   <P> Esta vez, el fichero petete contiene el texto siguiente:
   <P>
   <it/"b/&nl;
   <it/c/&nl;
   <it/a"/
   	<tscreen><verb>
	$sort /tmp/petete
	</verb></tscreen>

   <P> Esto es lo que muestra la salida:

   <P>
   <it/a/&nl;
   <it/b/&nl;
   <it/c/

   <P> Los comandos no deberían ser tan fáciles :-)

   <bf/bc/ (un lenguaje de programación de cálculos matemáticos)

   <P> Bc acepta cálculos desde la línea de comandos (entrada desde un 
fichero, pero no desde una redirección o una tubería), y también desde 
una interfaz de usuario. La siguiente demostración expone algunos de los 
comandos. Note que  ejecuto bc con el parámetro -q para evitar el mensaje 
de bienvenida.

   	<tscreen><verb>
   $bc -q
	</verb></tscreen>

   <P> <it/1 == 5/
   <P> <it/0/
   <P> <it/0.05 == 0.05/
   <P> <it/1/
   <P> <it/5 != 5/
   <P> <it/0/
   <P> <it/2 ^ 8/
   <P> <it/256/
   <P> <it/sqrt(9)/
   <P> <it/3/
   <P> <it/while (i != 9) {/
   <P> <it/i = i + 1;/
	<P> <it/print i/
   <P> <it/}/	
   <P> <it/123456789/
   <P> <it/quit/

   <bf/tput/ (inicializa una terminal o consulta la base de datos de
	terminfo)

   <P> Una pequeña demostración de las capacidades de tput:
   	<tscreen><verb>
	$tput cup 10 4
	</verb></tscreen>
   <P> La línea de comandos aparece en (y10,x4).
   	<tscreen><verb>
	$tput reset
	</verb></tscreen>
   <P> Limpia la pantalla y la línea de comandos aparece en (y1,x1).
	Observe que (y0,x0) es la esquina superior izquierda.
   	<tscreen><verb>
	$tput cols
	</verb></tscreen>
   <it/80/
   <P> Muestra el número de caracteres que caben en la dirección x.
   <P> Es muy recomendable familiarizarse con estos programas (al menos). 
Hay montones de programillas que le permitirán hacer virguerías en la 
línea de comandos.
   <P> [algunos ejemplos están copiados de las páginas man o los PUFs]
</sect1>
</sect>


<!-- Más scripts -->
<sect>
Más scripts
     <sect1>
     Aplicando un comando a todos los ficheros de un directorio.
     <P> 
     </sect1>
     <sect1>
     Ejemplo: Un script de copia de seguridad muy simple (algo mejor)
	   <P>
	   <tscreen><verb>
	    #!/bin/bash          
	    ORIG="/home/"
	    DEST="/var/copias_de_seguridad/"
	    FICH=home-$(date +%Y%m%d).tgz
	    tar -cZf $DEST$FICH $ORIG
	   </verb></tscreen> 
	   <P>
     </sect1>
     <sect1>
     Re-nombrador de ficheros
          <P>
	  <tscreen><verb>
          
             #!/bin/sh
             # renom: renombra múltiples ficheros de acuerdo con ciertas
             # reglas
             # escrito por Felix Hudson  Enero - 2000
             
             # primero comprueba los distintos 'modos' que tiene este
             # programa
             # si la primera ($1) condición coincide, se ejecuta esa parte
             # del programa y acaba
             
             # comprueba la condición de prefijo
             if [ $1 = p ]; then
             
             # ahora nos libramos de la variable de modo ($1) y ponemos $2
             # de prefijo
               prefijo=$2 ; shift ; shift
             
             # una rápida comprobación para ver si se especificó algún
             # fichero
             # si no, hay cosas mejores que hacer que renombrar ficheros
             # inexistentes!!
               if [$1 = ]; then
                  echo "no se especificaron ficheros"
                  exit 0
               fi
             
             # este bucle for itera a lo largo de todos los ficheros que
             # le hemos especificado al programa
             # renombra cada uno de ellos
               for fichero in $*
                 do
                 mv ${fichero} $prefijo$fichero
               done
             
             # ahora salimos del programa
               exit 0
             fi
             
             # comprueba si es un renombramiento con sufijo
             # el resto es casi idéntico a la parte anterior
             # lea los comentarios anteriores
             if [ $1 = s ]; then
               sufijo=$2 ; shift ; shift
             
                if [$1 = ]; then
                 echo "no se especificaron ficheros"
                exit 0
                fi
             
              for fichero in $*
               do
                mv ${fichero} $fichero$sufijo
              done
             
              exit 0
             fi
             
             # comprueba si es una sustitución
             if [ $1 = r ]; then
             
               shift
             
             # he incluído esto para no dañar ningún fichero si el
             # usuario no especifica que se haga nada
             # tan sólo una medida de seguridad             
               if [ $# -lt 3 ] ; then
                 echo "uso: renom r [expresión] [sustituto] ficheros... "
                 exit 0
               fi
             
             # elimina el resto de información
               VIEJO=$1 ; NUEVO=$2 ; shift ; shift
             
             # este bucle for itera a lo largo de todos los ficheros que
             # le hemos especificado al programa
             # renombra cada fichero utilizando el programa 'sed'
             # es un sencillo programa desde la línea de comandos que
             # analiza la entrada estándar y sustituye una expresión por
             # una cadena dada
             # aquí le pasamos el nombre del fichero (como entrada
             # estándar)
               for fichero in $*
               do
                 nuevo=`echo ${fichero} | sed s/${VIEJO}/${NUEVO}/g`
                 mv ${fichero} $nuevo
               done
             exit 0
             fi
             
             # si se llega a esta parte es que no se le pasó nada
             # apropiado al programa, por lo que le decimos al usuario
             # cómo hacerlo
             echo "uso:"
             echo " renom p [prefijo] ficheros.."
             echo " renom s [sufijo] ficheros.."
             echo " renom r [expresión] [sustituto] ficheros.."
             exit 0
             
             # hecho!
             
	  </verb></tscreen>
     </sect1>
     <sect1>
     Re-nombrador de ficheros (sencillo)
     <p>
     <tscreen><verb>
     #!/bin/bash
     # renombra.sh
     # renombrador de ficheros básico

     criterio=$1
     expresion=$2
     sustituto=$3
     
     for i in $( ls *$criterio* ); 
     do
         orig=$i
         dest=$(echo $i | sed -e "s/$expresion/$sustituto/")
	 mv $orig $dest
     done
     </verb></tscreen>
     </sect1>
</sect>

<sect>
Cuando algo va mal (depuración)
     <sect1>
     Maneras de llamar a BASH 
          <p> Una buena idea es poner esto en la primera línea:
	  <tscreen><verb>
	  #!/bin/bash -x
	  </verb></tscreen>
	  <P> Esto producirá información interesante.
     </sect1>
</sect>

<sect>
Sobre el documento
     <P> Siéntase libre para hacer sugerencias/correcciones, o lo que
	crea que sea interesante que aparezca en este documento. Intentaré
	actualizarlo tan pronto como me sea posible.
     <sect1>
     (sin) Garantía
          <P> Este documento no lleva garantía de ningún tipo.
     </sect1>
     <sect1>
     Traducciones
     	<p> Italiano: por William Ghelfi (wizzy está en tiscalinet.it).
	<htmlurl name="http://web.tiscalinet.it/penguin_rules"
	url="http://web.tiscalinet.it/penguin_rules">
     	<p> Francés: por Laurent Martelli 
		<htmlurl name="¿?" url="http://">
	<p> Coreano: Minseok Park 
		<htmlurl url="http://kldp.org" name="http://kldp.org">
	<p> Corean: Chun Hye Jin 
		<htmlurl url="" name="Desconocido">
	<p> Spanish: Gabriel Rodríguez Alberich
		<htmlurl url="http://www.insflug.org" name="http://www.insflug.org">
     <p> Supongo que habrá más traducciones, pero no tengo información
	sobre ellas. Si las tiene, por favor, envíemelas para que actualice esta
	sección.
	
     </sect1>
     <sect1>
     Agradecimientos
     <P>
     <itemize>
     	<item> A la gente que ha traducido este documento a otras lenguas
	(sección anterior).
	<item> A Nathan Hurst por enviar montones de correcciones.
	<item> A Jon Abbott por enviar comentarios sobre la evaluación de
	expresiones aritméticas.
	<item> A Felix Hudson por escribir el script <it/renom/
	<item> A Kees van den Broek (por enviar tantas correcciones y reescribir
	la sección de comandos útiles)
	<item> Mike (pink) hizo algunas sugerencias sobre la localización del
	bash y la comprobación de los ficheros
	<item> Fiesh hizo una buena sugerencia sobre la sección de bucles.
	<item> Lion sugirió mencionar un error común (./hello.sh: Comando no
	encontrado.)
	<item> Andreas Beck hizo varias correcciones y comentarios.
     </itemize>
     </sect1>
	<sect1>
	Historia
	<p> Añadidas nuevas traducciones y correcciones menores.
	<p> Añadida la sección de comandos útiles reescrita por Kess.
	<p> Incorporadas más correcciones y sugerencias.
	<p> Añadidos ejemplos sobre la comparación de cadenas.
	<p> v0.8 abandono del versionamiento. Supongo que con la fecha es
	suficiente.
	<p> v0.7 Más correcciones y algunas secciones TO-DO escritas.
	<p> v0.6 Correcciones menores.
	<p> v0.5 Añadida la sección de redireccionamiento.
	<p> v0.4 desaparición de su sitio debido a mi ex-jefe. Este documento
	tiene un nuevo sitio en: http://www.linuxdoc.org.
	<p> Anteriores: no me acuerdo y no he usado rcs ni cvs :(
	</sect1>

     <sect1>
     Más recursos
          <P>
	  <P> Introducción a bash (bajo BE)
	      <htmlurl url="http://org.laol.net/lamug/beforever/bashtut.htm"
		name="http://org.laol.net/lamug/beforever/bashtut.htm">
          <P> Programación en Bourne Shell
	      http://207.213.123.70/book/ 
     </sect1>
</sect>

</article>
