RAFA !!
Opciones Curso Básico de Linux
Copiado el 17 de Febrero del 2004
(http://www.terra.es/personal/garzones/shell-scripts-micro-como.html)


1.- Introducción.
        1.1 El Shell
        1.2 Tipos de Shell
        1.3 Procesos
        1.4 Como ejecutar un guión-shell
2.- El Shell como intérprete de órdenes
        2.1Entrada y salida estándar
        2.2 Tuberías.
        2.3Otros operadores.
3.- El shell como lenguaje de programación
        3.1 Variables de entorno.
                3.1.1 Asignación de un valor a una variable.
                3.1.2 Acentos graves.
                3.1.3 Comillas dobles y simples.
                3.1.4 La orden export.
                3.1.5 Paso de parámetros.
        3.2Instrucciones condicionales.
        3.3 Estructuras de control
                3.3.1 Sentencia if
                3.3.2 Sentencia case
                3.3.3 Sentencia while y until
                3.3.4 Sentencia for
        3.4 #!/bin/sh
         3.5 Funciones.
4.- Mas información


1.- Introducción

    1.1.- El shell

        El shell es un programa cuyas funciones podemos definir como:

        a) Intérprete de órdenes del sistema operativo (S.O.). Acepta los comandos y se encarga de su ejecución.  Permite la comunicación entre el usuario y el S.O. El shell recibe el comando y lo analiza; si es un comando interno de la shell (cd, echo, set, eval, etc.) lo ejecuta, en caso contrario lo busca en el sistema de ficheros ( /bin /sbin /usr/bin /usr/sbin ...), cede el control al kernel para que los ejecute, y devuelve los resultados al usuario.

       Cuando ejecutamos un comando el shell busca el fichero en los directorios especificados en la variable de entorno PATH, no mira en el directorio actual. Si estamos en el directorio y queremos ejecutar el shell script miConfig tendremos que escribir

    # ./miConfig

        Para evitar esto basta con editar el fichero /etc/bashrc u otro script de inicio y añadir a la variable PATH un punto.

/etc/bashrc
--------
.......
PATH=:.
.......

        Si hubiese otro fichero llamado miConfig en los directorios especificados en PATH, p.e. en /bin, ejecutará éste y no el situado en , ya que el punto lo hemos puesto en último lugar.(En MS-DOS primero busca en el directorio actual y despues en los directorios especificados en el PATH).

    Se entiende por el directorio raiz del usuario.

        b) Lenguaje de Programación, presenta características de lenguaje de programación interpretado de alto nivel a escala completa. Es el equivalente en concepto a los .BAT de MS-DOS pero mucho más completo y poderoso. Posee estructuras de control if, case, for, while, variables, parámetros, etc. A los programas shell se les suele denominar shell-scripts o guiones shell.

        Un guión-shell es un fichero que contiene uno o más comandos. Para crear el fichero se utilizará alguno de los editores del S.O. tales como vi, emacs, etc. El interprete shell ejecutará las instrucciones una a una o bifurcará a la instrucción que corresponda según la condición especificada en una estructura de control. Si se encuentra un error interrumpe la ejecución.

        Numerosas herramientas de administración del sistema están escritas con guiones shell.

        El shell permite que cada usuario particular cree su propio entorno, adecuado a sus necesidades de trabajo e independiente del entorno del resto de los usuarios del sistema.

    1.2 - Tipos de Shell

        Linux soporta varios intérpretes de órdenes o shells. Hasta que en 1983 ATT estandarizó el shell, cada UNIX creó su propia versión con características particulares. sh, llamado Bourne Shell es el shell estandarizado por ATT, y bash es el más usado en linux ya que incorpora lo mejor de sh, ksh y tcsh. Cuando un usuario entra en el sistema, se le asigna un shell para trabajar con el S.O. En /etc/passwd a cada usuario se le asigna el interprete de ordenes que va a utilizar para comunicarse con el S.O.

/etc/passwd
garza:x:14500:14000:Garza:/home/garza:/bin/bash

    1.3 - Procesos

        Al conectarse el usuario garza se ejecuta el interprete de comandos y crea un proceso de conexión (proceso padre de usuario). Todos los procesos que se creen a continuación, por parte del usuario, serán procesos hijos del proceso padre, éste pasa a un estado de espera y cuando el proceso hijo finaliza, el proceso padre pasa al estado activo y aparece de nuevo el prompt.

        A cada proceso generado en el sistema, se le asigna un número de identificación conocido como PID y un nombre que corresponde al del programa activo dentro de él.  Cada proceso creado en el sistema tendrá como dueño al  usuario que lanzó la ejecución del comando original. Todos los procesos tienen un proceso padre identificado con PPID, si haces un ps -lx | more verás que init tiene como PPID un 0, init es el proceso padre de todos los procesos.

    1.4.- Como ejecutar un guión-shell.

        Veamos un ejemplo práctico, creamos con nuestro editor favorito el siguiente shell script:

         $  vi miConfig

        alias rm="rm -i"
        alias cp="cp -i"
        clear
        pwd
        df -a
        PATH=;/home/garza/HTML

         Puede ser ejecutado de varias formas:

        a) $ sh miConfig

        b) $ miConfig
       Es necesario que el fichero miConfig tenga permisos de ejecución.

        c) $ .  miConfig
        Al igual que el caso anterior necesita permisos de ejecución, el punto hace que al finalizar el guión-shell no volvamos al entorno de trabajo en el que nos encontrabamos, sino que conservaremos el entorno que nos ha dejado el programa miConfig. El proceso padre hereda el entorno del proceso hijo.  (Entendemos por entorno el conjunto de variables y  funciones del shell). En realidad el punto lo que hace es que no se crea un proceso hijo. En el ejemplo, al finalizar la ejecución de .  miConfig los alias y la variable de entorno PATH queda con el valor especificado en el guión.  En los ejemplos a) y b), al finalizar la ejecución la variable PATH queda con el valor que tuviese antes de la ejecución.
 

    2. El Shell como intérprete de órdenes

    2.1 Entrada y salida estándar.

        El canal de entrada para una orden se conoce como entrada estándar (stdin) y el canal de salida se denomina salida estándar (stdout), también existe un canal de salida para los errores (stderr). Generalmente las órdenes se introducen mediante el teclado  (stdin) y su resultado se visualiza en la pantalla  (stdout), sin embargo, se puede redirigir la entrada y la salida estándar a ficheros. Ejemplos :

        $ cat /etc/passwd > /etc/passwd.seg
El operador > redirecciona la salida estándar a un fichero, en este caso llamado passwd.seg, si no existe lo creará y si existe lo destruye antes de escribir la salida de la orden en él.

        $ echo "garza:x:14500:14000:GarZa:/home/garza:/bin/sh" >> /etc/passwd
El operador >> es idéntico al anterior, sólo que no es destructivo, la salida de echo se añade al final del fichero /etc/passwd.

        $ mail rperez < nota
El operador < redirige la entrada estándar para que provenga de un fichero, en el ejemplo el fichero nota es la entrada de la orden mail rperez

        $ cat nota1 - nota3 > nota4
El operador - es utilizado para tomar la entrada estándar en ese punto. En el ejemplo, cat procesa nota1,  pasa a la entrada desde teclado, y procesa nota2, y las tres entradas se redirigen al fichero nota4.

        $ cat nota1 2> /dev/null
Si el fichero nota1 no existe devolverá a la pantalla un mensaje de error, si no queremos ver los errores utilizar el canal de salida para errores, 2>. En este caso al redireccionar a /dev/null el mensaje de error se destruye, si nos interesa conocer el error podemos redireccionar a un fichero y procesarlo más tarde.

    2.2 Tuberías.

        Antes veíamos como redireccionar la entrada o salida estandar a un fichero, ahora lo que vamos a ver como la salida de una orden se puede dirigir a la entrada de otra orden. Esto se hace mediante el operador | llamado pipeline.

        $ ps aux | grep "httpd"
        La salida de la orden ps es procesada por la orden grep, en pantalla sólo aparecerán los procesos httpd.

    2.3 Otros operadores

        Hay numerosos caracteres que poseen un significado especial para el shell:

        *       Sustituye cualquier grupo de caracteres, excepto cuando el primer carácter del grupo es un punto. p.e.
                # ls -l PRO* 'lista todos los archivos que su nombre empiecen por PRO

        ?       Sustituye Un carácter.
                # ls ??R 'lista todos los archivos que en su tercera posición del nombre tengán una R.

        &      Indicador de procesos background.

              Separador de comandos.  Pueden ejecutarse varios comandos en la misma línea, separados del operador ;
                   p.e.
                # clear ; ls -la

        \        Indica que el comando sigue en la siguiente línea.

        $       Hace referencia al contenido de una variable.

        #         comentarios, son lineas que no son procesadas en un guión shell.

        `comando`    Acentos graves. La salida estándar de un comando se le asigna a una variable de entorno.

        "cadena"  o 'cadena'  Para cadenas.

    3.- El shell como lenguaje de programación

    3.1.- Variables del entorno

        3.1.1 Asignación de un valor a una variable

        Son cadenas de caracteres de la forma NOMBRE_VARIABLE=valor. El nombre de la variable puede ser cualquier cadena de caracteres que no incluya el signo $ y que no lleve espacios en blanco. Por convenio el nombre se escribe en mayúsculas pero no es necesario. El valor puede ser cualquier cadena de caracteres incluidos espacios en blanco, en tal caso deberá ponerse entre comillas. p.e.:

        # CIUDAD=Jaen
        #CIUDAD="Ciudad Real"
        # CODPOSTAL=14013

        Para visualizar el valor de una variable hay que utilizar el caracter especial $:

        # echo
        Ciudad Real
        #

        Al declarar una variable si ya está en uso, el shell cambiará el valor de esa variable por el nuevo. En el anterior ejemplo el valor de la variable CIUDAD es Ciudad Real, el valor Jaen ha sido destruido por el shell.

        En un guión shell para asignar un valor a una variable mediante el teclado usar la orden read:

        echo "Introduzca su nombre "
        read nombre
        echo

        La entrada por teclado es asignada a la variable nombre.

        3.1.2 Acentos graves.

        Mediante los acentos graves podemos asignar a una variable el resultado de la ejecución de un comando:

         # ESTOY_EN=`pwd`
         # echo
         /home/garza
        #

        Ejecuta el comando pwd y el resultado lo asigna a la variable ESTOYEN.

       Al conectarnos a nuestro login y ejecutar la orden env nos aparecerá en pantalla una lista de variables de entorno, éstas están asignadas por el sistema y son utilizadas por el shell y por las aplicaciones.

        3.1.3.- Comillas dobles y simples

        Veamos dos ejemplos:

    # QUIENSOY="Soy el usuario "
    # echo
    Soy el usuario garza
    #

    La variable de entorno es sustituida por su valor al utilizar el $ y comillas dobles

    # COM_SIMPLES='La variable   es una variable asignada por el sistema'
    # echo
    La variable es una variable asignada por el sistema

    Las comillas simples impiden que sea evaluada, es tomada como cadena de caracteres.

        3.1.4.- El comando export

            El comando export se utiliza para que el valor asignado a una variable sea conocido por todos los procesos hijos del proceso padre. p.e.

        # APF=/opt/apf/bin

        Si a continuación ejecutamos un guión shell que use la variable APF, fallará ya que no la conoce.

        # APF=/opt/apf/bin
        # export APF

        Todos los procesos hijos heredan las variables exportadas del proceso padre.

      3.1.5.- Paso de Parámetros

        Son variables cuyo valor es pasado a un guion shell para su posterior proceso. Los nombres de estas variables son $1, $2 ... hasta $9. Creamos el siguiente guión shell y le asignamos permiso de ejecución:

        imprime
        ---------
        echo $1
        echo $2

        Desde el shell ejecutamos el guión y le pasamos dos valores de variables que son procesadas por el guión mediante $1 y $2.

        # imprime hola mundo
        hola
        mundo
        #

        $# - indica el número de parámetros pasados al guión shell.
        $* - devuelve los valores de todos los parámetros
        $0 - devuelve el nombre del guión shell

        Ejemplos:

        1) Devuelve el número de parámetros (10) y los lista.
             cuenta 1 2 3 4 5 6 7 a b c
            -----------------------
            echo "Número de parámetros $#"
            for NUM in $*
            do
                echo
            done

    3.2.- Instrucciones condicionales

        El shell, como lenguaje de programación, nos permite evaluar expresiones y hacer depender la ejecución del programa de dicha evaluación. Existen distintas estructuras de control que permitirán llevar a cabo ejecuciones no secuenciales sino en función de la evaluación de condiciones. (if, case, while, until, for, ...).

        Para evaluar una condición podremos usar:

        a) test <expresión>

        b) [ expresión ]

         Los corchetes deben de estar rodeados por espacios en blanco, al igual que los operadores de la expresión.

        En ambos casos, se comprobará la expresión, y si es cierta devolverá un 0 y si es falsa un valor distinto de 0. Éste valor se almace en la variable especial $?.

        Posibles expresiones:

        Para ficheros:

            -r <fichero>  Es Verdadero si el fichero existe y se puede leer
            -w <fichero>  Es Verdadero si el fichero existe y se puede escribir en el
            -x <fichero>  Es Verdadero si el fichero existe y es ejecutable
            -f <fichero>  Es Verdadero si el fichero existe
            -d <directorio> Es Verdadero si es un directorio
            -s <fichero>  Es Verdadero si el fichero existe y tiene un tamaño mayor que cero.

        Para cadenas:

            -z cadena  Es Verdadero si la longitud de la cadena es cero.
            -n cadena  Es Verdadero si la longitud de la cadena es distinta de cero.
            cadena1  =  cadena2 Verdadero si las dos cadenas son iguales (atención a los espacios en blanco).
            cadena1 != cadena2 Verdadero si las cadenas son distintas (atención a los espacios en blanco).
            cadena  Verdadero si la cadena no es nula

       Para operaciones aritméticas

            -eq Igual
            -ne Distinto
            -gt Mayor que
            -lt Menor que
            -ge Mayor o igual
            -le Menor o igual

      Operadores lógicos

            AND: && ó -a
            OR: || ó -o
            NOT: !

            La orden true devuelve un 0, se emplea en estructuras repetitivas.
            La orden  false devuelve un valor distinto de 0, igual que true se emplea en estructuras repetitivas.

            Ejemplos:

            1) Si la variable existe y no es nula entonces devuelve un 0 y se ejecuta la rama then, en caso contrario devuelve un valor distinto de 0 y se ejecuta la rama else

            if test ; then
                echo "Variable FIG definida"
            else
                echo "Variable FIG no definida"
            fi

            2) Igual ejemplo que el anterior pero usando corchetes

            if [    ] ; then
                echo "Variable FIG definida"
            else
                echo "Variable FIG no definida"
            fi

            3)  Comprueba el valor de retorno, si mkdir se ejecuta con exito devuelve un 0, caso contrario un valor distinto de cero.

          mkdir /home/informat
            RETORNO=$?
            if [ = 0  ]
            then
                echo "Directorio creado"
            else
                echo "No se pudo crear el directorio"
            fi

        La orden expr además de utilizarse para operaciones aritméticas, también se emplea para evaluar expresiones, disponemos de los siguientes operadores:

            =     igual
            !=   distinto
            \>    mayor que
            \<    menor que
            \>=  mayor o igual que
            \<=  menor o igual que

            Si la condición es cierta devuelve un 1 y si es falsa un 0.

            Ejemplo

            a=3
            echo -n "Introduzca un valor del 1 al 5"
            read respuesta
            if [ `expr = ` = 1) ; then    ;#equivalente a if [ = ) ; then
                echo "Ha acertado, ha pulsado el número 3"
            else
                echo "Número incorrecto, vuelva a intentarlo"
            fi

    3.3.- Estructuras de control

        3.3.1 Sentencia if

        if <condición>
        then
                inst1
                inst2
                ....
        fi

        Si se cumple la condición se ejecutarán el bloque de instrucciones de la rama then.

        If <condición>
         then
                inst1
                inst2
                ...
        else
                inst10
                inst11
                ....
        fi

        Se evalúa la condición y si se cumple se ejecutan las instrucciones de la rama then, si no se cumple se ejecutan las instrucciones de la rama else.

        if <condición1>
        then
                instrucciones1
        elif <condicion2>
            then
                instrucciones2
        else
                instrucciones3
        fi

        Se evalúa condición1, si se cumple se ejecuta la rama del then, si no, se evalúa condición2, si se cumple se ejecutarán las instrucciones2 y si no se cumple la rama else (instrucciones3). elifes equivalente a un else if:

        if <condición1>
        then
                instrucciones1
        else
                if  <condicion2>
                then
                    instrucciones2
                else
                    instrucciones3
                fi
        fi

        3.3.2 Sentencia case

        case $variable in
            valor1) instruccion1;insruccion2; ...;;
            valor2) instruccion1
                           instruccion2
                           instruccion3;;
            *)  instruccion44;....;;
        esac

        Evalúa el valor de una variable y ejecuta varias instrucciones en función de su valor. Es equivalente a usar ifs anidados, aunque más completo y organizado. Si la variable no es igual a ninguno de los valores especificados, se ejecutan las instrucciones especificadas en el asterisco.
 

         3.3.3 Sentencias while y until

        while <condición>
        do
                instrucción1
                instrucción2
                .....
        done

        until <condición>
        do
                instrucción1
                instrucción2
                .....
        done

       Usando true podemos crear bucles infinitos, para salir del bucle utilizaremos la orden break,  la orden exit finaliza la ejecución de un guión shell y sleep <segundos>  hace una pausa por el tiempo indicado.

        while true
        do
            .......
            if  [ = 1 ]
                then
                break
            fi
        instrucción10.
            .....
        done
        instrucción200
        .......
        .......

        El bucle se ejecutará hasta que la variable sea igual a 1, entonces se el shell procesará la orden break y pasará a ejecutar la siguiente instrucción en la que termina el bucle. (instrucción200).

        También podemos interrumpir la ejecución de un bucle, y hacer que vuelva al principio del mismo, mediante la orden continue.

        Ejemplos:

        1) Ejemplo con un while true y case

while true
do
    clear
    echo;echo;echo;echo;echo
    echo "--------------------------------------------------"
    echo "  MANTENIMIENTO DE USUARIOS   "
    echo "--------------------------------------------------"
    echo "        1 - Alta de usuarios "
    echo "        2 - Baja de usuarios "
    echo "        3 - Salir"
    echo "--------------------------------------------------"
    echo -n "Elija una opción > "
    read respuesta
    case in
        1) instrucciones_alta;;
        2) instrucciones_baja;;
        3) break;;
        default) echo "Opción no válida"
                        sleep 4
                        ;;
    esac
done

        2)  Lee el fichero y lo procesa fila a fila.

        while read fila
         do
                echo
         done < nombre_fichero

        3.3.4. Sentencia for

        for variable in valor1 valor2 valor3 valor4 ....
        do
            instrucción1
            instrucción2
            ......
        done

            Ejemplos:

            1) La varible num irá tomando los valores indicados en in, y el bucle se repetirá tantas veces como valores especificados. Imprime la lista 1 2 3 4 5, el bucle se repetirá 5 veces.

          for num in 1 2 3 4 5
            do
                echo
            done

            2)  El * sustituye a los ficheros del directorio actual.

          for VAR in *
            do
                echo
            done

    3.4 #!/bin/sh

                En la primera línea de los guiones shell, suele emplearse la orden #!/bin/sh , esto fuerza a que el guión se ejecute bajo el shell sh, con esto se garantiza que su ejecución sea independiente del shell que haya elegido el usuario, siempre se ejectarán con el intérprete sh.

    3.5 Funciones

                Son guiones shell pero que en vez de almacenarse en un fichero se carga en el entorno. Ejecutando la orden set veremos las variables del entorno y funciones definidas. Para crear una función hay dos métodos:

        a)    Desde el shell:

            # nombre_funcion () {
                >    instrucción1
                >    instrucción2
                >    ......
                >}
                #

      b)    Almacenar la función en un archivo:

                Ejemplos:

                1) Crea una función desde la shell

                    # busca () {
                    > find . -name $1 -print | more
                    > }
                    #

                2) Creamos el fichero fun y escribimos en él:

            busca () {
                    find . -name $1 -print | more
                    }

                    Para cargar la función en memoria (no olvidar el punto):

            # . fun

                    Para usar la función, independientemente del método usado para crearla:

                     # busca <fichero>

        Es habitual añadir nuestras funciones a /etc/bashrc, de modo que se carguen en el entorno cuando el usuario se identifique.

4.- Más información en las páginas man: man bash, man for, man while, man if, etc.