Capítulo 11. Interprete de Comandos

Tabla de contenidos

Shell Scripting
Algunas shells
Creando shell scripts
Ejemplo de un shell script
Planificación de Tareas
at
cron
anacron

Para la relación con el sistema el administrador debe utilizar un interface. Evidentemente en los sistemas modernos los interfaces visuales son la principal forma de dar ordenes al ordenador, pero en Unix y en Linux el administrador debería conocer también un interface textual.

Cuando se utiliza en linea para introducir directamente comandos se denomina shell interactiva.

Para el administrador, el interprete de comandos (shell) y "guiones" del interprete de comandos (shell scripts) son muy importantes por varias razones:

También veremos algunos servicios donde se utilizan habitualmente los shell script.

Shell Scripting

Los shell scripts son ficheros de texto que contienen comandos de sistema, comandos propios del interprete de comandos y estructuras de control necesarias para procesar el flujo del programa (tipo while, for, etc). Los ficheros script son directamente ejecutables por el sistema bajo el nombre que se haya dado al fichero. Para ejecutarlos, se invoca el shell junto con el nombre del fichero, o bien se dan permisos de ejecución.

La programación en shell es muy útil y cómoda para crear programas fácilmente modificables, pequeños, no complejos, que resuelvan tareas repetitivas, típicas de los administradores. Además, es un lenguaje preparado para manejar ristras y procesar y filtrar texto, por lo que es mucho más fácil programar en shell, que, por ejemplo, en C.

Algunas shells

Un inconveniente es que no es un lenguaje estandarizado si no que hay varias versiones del shell.

Algunos de los más comunes son:

  • El shell Bourne (sh). El shell estándar UNIX, y el que todos los UNIX poseen en alguna versión, en linux es un bash renombrada. El sh fue creado por Stephen Bourne en AT&T a finales de los setenta. El prompt por defecto suele ser un '$' y en usario root '#'.

  • El shell Bash (bash). El shell Linux por defecto. Deriva de la bourne shell pero se ha impuesto en gran medida por su utilización en Linux.

  • El shell Korn (ksh). Es una mejora del Bourne, escrito en AT&T por David Korn en los años ochenta, intenta combinar la sencillez del Bourne con la eficacia de la shell C, más algún añadido. El prompt por defecto es el $.

  • El shell C (csh). Fue desarrollado en la Universidad de Berkeley por Bill Joy a finales de los setenta y tiene unos cuantos añadidos interesantes al Bourne, como un histórico de comandos, alias, aritmética desde la línea de comandos, completa nombres de ficheros y control de trabajos en segundo plano. El prompt por defecto para los usuarios es `%'. Una ventaja de los scripts en C shell es que, como su nombre indica, su sintaxis está basada en el lenguaje C. Como shell posteriores recogen las mejoras de esta, hace que no se utilice mucho, aunque todavía se encuentran muchos scripts desarrollados para esta shell.

  • Existen muchas otras que son variantes de estas, normalmente versiones reducidas con aplicaciones específicas.

Creando shell scripts

Cada shell cambia un poco el lenguaje pero tienen muchas características comunes. Vamos a ver un resumen de la sintaxis del lenguaje:

  1. Los comentarios se comienzan con #. En la primera linea se debe escribir #! con la shell que (o incluso un interprete, como perl o php) con la que queremos ejecutarla, por ejemplo:

    #!/bin/bash
  2. Para realizar redirecciones de los programas se utilizan > para salida, < para entrada, &< para salida de error y |túnel (pipe).

    cat laza.txt |wc -l > lineas_laza.count

    La salida del comando cat que es le fichero laza.txt se le pasa al comando wc -l que cuenta las lineas y lo mete en el fichero lineas_laza.count.

  3. Para definir variables se debe poner el nombre seguido de igual y su valor. Para referenciarlas con el símbolo dolar ($). Existen variables predefinidas, como $1 para el primer parámetro del shell script, $HOME directorio home de usuario, $? código de salida de programa recién ejecutado y muchas más dependiendo de la shell.

    FILE=/tmp/salida
    cat laza.txt | wc -l >> $FILE

    Crea la variable FILE poniendo un nombre de fichero y la utiliza para añadir la salida del resultado del contador de lineas.

  4. Hay tres tipos de comillas, las dobles interpretan las variables que hay dentro, las simples no, y la comilla invertida ejecuta su contenido como un comando y lo mete en la variable.

    DATE=`date +%d-%m-%Y`;
    MSG1="La fecha es $DATE";
    MSG2='La variable donde guardo la fecha se llama $DATE con el comando
    date +%d-%m-%Y';
    echo $DATE;
    echo $MSG1;
    echo $MSG2;
    

    Para ejecutar este script:

    [pcm@sal]# sh comillas.sh
    14-04-2007
    La fecha es 14-04-2007
    La variable donde guardo la fecha se llama $DATE con el comando date
    +%d-%m-%Y
    [pcm@sal]# 
    
  5. Para las shell la condición verdadera es el 0 y el resto lo interpreta como falso. Existen bastantes operadores para realizar las condiciones. Pueden hacerse condiciones sobre fichero: si es un fichero (-f), si es un directorio (-d), si hay permiso de lectura (-r). También sobre cadenas, sobre números y combinar condiciones.

    Por ejemplo [ -d .ssh -a \( -n $JDK_HOME -o -n $JAVA_HOME \) ] nos devolvería como verdadero si existe el directorio .ssh y alguna de las dos variables no deben ser vacías.

  6. Para el control de flujo tenemos las estructuras if, case, while, for y until.

  7. Existen un conjunto de herramientas que son muy utilizadas en los shell script, como pueden ser cut, grep, sed, awk, date, etc...

  8. Para hacer debug podemos chequear la sintaxis del shell script con:

    sh -n mishell.sh

    También podemos hacer que nos muestre la ejecución de los comandos que hay en el shell script y los valores que van tomando las variables con:

    sh -x mishell.sh
    

Ejemplo de un shell script

Como ejemplo de programa shell script vamos a hacer una utilidad para buscar ficheros de texto de DOS en el directorio actual y preguntarnos si lo queremos convertir a fichero de texto UNIX. Los ficheros de texto en la plataformas DOS/Windows para finalizar cada linea llevan dos caracteres de control, el ascii 10 (LF) y el ascii 13 (CR). En cambio en UNIX, y por tanto el Linux los fichero de linea sólo utilizan el carácter de control ascii 10 (LF).

La mayor parte de los editores de Linux ya distinguen si es un texto de DOS o Unix. Además existe un comando para realizar esta conversión, dos2unix. Por lo que no suele hacer falta una shell para esta tarea, a no ser que no dispongamos del conversor en el sistema. El programa sería:

#!/bin/bash
for fichero in *.txt; do
  if grep ^M $fichero &>/dev/null; then
     resp=x
     while [ $resp != "s" -a $resp != "n" ]; do
       echo "'$fichero' es un fichero texto DOS. convertir? (s/n) "
       read resp
     done
     case $resp in
       s)
         sed 's/^M//' $fichero > /tmp/FILE_TMP
         mv /tmp/FILE_TMP $fichero
         echo "El fichero '$fichero' convertido a texto UNIX";;
       n)
         echo "El fichero '$fichero' se deja texto DOS";;
       *)
         echo "ERROR";;
     esac
   fi
done

Primeramente ponemos el comentario para indicar que es un script para bash.

El for nos va a realizar un bucle por todos los fichero que terminen en .txt.

Hacemos una condición que con el comando grep nos mire si el fichero tiene lineas con carácter ascii 13 (CR). Para introducir el carácter ^M hemos pulsado Control+V y Control+M, no se escribe con ^ y la M. Este comando si no encuentra ninguna linea devuelve 1, y si encuentra al menos una linea devuelve cero, con lo cual cumplimos la condición.

A continuación vamos a pedir al usuario que nos confirme la conversión. Para ello ponemos por pantalla la pregunta y con el comando read cogemos el valor introducido. Con un while insistimos con la pregunta mientras la contestación no sea s o n.

Con case comprobamos que ha metido. Sería más lógico hacerlo con un if else, pero así vemos está estructura. Sí selecciono n se imprime por pantalla que no se hizo nada con el fichero.

Cuando opto por convertir el fichero utilizamos la herramienta sed que mediante expresiones regulares nos permite hacer sustituciones dentro de un fichero de texto. En este caso le estamos diciendo que sustituya los CR por nada. La salida la redirigimos a un fichero temporal que luego sustituye al original.

Para ejecutar el programa tendríamos dos posibilidades, o bien lo hacemos ejecutable con el comando chmod u+w txtunixdir.sh y luego lo arrancamos como ./txtunixdir.sh o bien le pasamos a una shell como parámetro nuestro programa:

[pcm@sal]# sh txtunixdir.sh
cursos.txt' es un fichero texto DOS. convertir? (s/n)
n
El fichero 'cursos.txt' se deja texto DOS
'lazae11.txt' es un fichero texto DOS. convertir? (s/n)
s
El fichero 'lazae11.txt' convertido a texto UNIX
[pcm@sal]# 

Si al escribir el programa nos hubiésemos dejado sin poner los dos puntos y coma en las opciones del case tendríamos un error, que antes de ejecutar nos lo advertiría.

...
  n)
    echo "El fichero '$fichero' se deja texto DOS"
...

Si solo queremos comprobar sin ejecutar lo podríamos hacer con la opción -n.

[pcm@sal]# sh -n txtunixdir.sh
txtunixdir.sh: line 16: syntax error near unexpected token `)'
txtunixdir.sh: line 16: `       *)'
[pcm@sal]# 

Corregimos de nuevo, y ahora ejecutamos pero con la opción -x.

[pcm@sal]# sh -x txtunixdir.sh
+ grep $'\r' cursos.txt
+ resp=x
+ '[' x '!=' s -a x '!=' n ']'
+ echo ''\''cursos.txt'\'' es un fichero texto DOS. convertir? (s/n) '
'cursos.txt' es un fichero texto DOS. convertir? (s/n)
+ read resp
s
+ '[' s '!=' s -a s '!=' n ']'
+ sed $'s/\r//' cursos.txt
+ mv /tmp/FILE_TMP cursos.txt
+ echo 'El fichero '\''cursos.txt'\'' convertido a texto UNIX'
El fichero 'cursos.txt' convertido a texto UNIX
+ grep $'\r' iptables.txt
+ grep $'\r' lazae11.txt
[pcm@sal]#