miércoles, 7 de junio de 2017

Una aplicación más completa

Tras un largo paréntesis vamos a retomar nuestro trabajo con nuestro programa sys_info_page. Nuestra próxima mejora añadirá al programa varias opciones de línea de comandos de la siguiente manera:
  • Archivo de salida. Añadiremos una opción para especificar un nombre para un archivo que contenga la salida del programa. Se especificará como -f archivo o como --file archivo.
  • Modo interactivo. Esta opción preguntará al usuario un nombre para el archivo de salida y comprobará si el archivo especificado ya existe. Si es así, se le preguntará al usuario antes de que el archivo existente se sobrescriba. Esta opción se especificará como -i o como --interactive.
  • Ayuda. Tanto -h como --help pueden especificarse para hacer que el programa produzca un mensaje informativo de uso.
Aquí está el código necesario para implementar el procesado en la línea de comandos:

usage () {
    echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
    return
}

# process command line options

interactive=

filename=

while [[ -n $1 ]]; do
    case $1 in
        -f | --file)        shift
                            filename=$1
                            ;;
        -i | --interactive) interactive=1
                            ;;
        -h | --help)        usage
                            exit
                            ;;
        *)                  usage >&2
                            exit 1
                            ;;
    esac
    shift
done

Primero, añadimos una función de shell llamada usage para mostrar un mensaje cuando se invoca la opción de ayuda o se intenta una opción desconocida.

A continuación, comenzamos el bucle de procesamiento. Este bucle continúa mientras que el parámetro posicional $1 no esté vacío. Al final del bucle, tenemos un comando shift para avanzar los parámetros posicionales y asegurarnos de que el bucle finalmente terminará.

Dentro del bucle, tenemos una sentencia case que examina el parámetro posicional actual para ver si coincide con alguna de las opciones soportadas. Si encuentra un parámetro soportado, se actúa sobre él. Si no, se muestra el mensaje de uso y el script termina con un error.

El parámetro -f se maneja de una forma interesante. Cuando se detecta, hace que ocurra un shift adicional, que avanza el parámetro posicional $1 al argumento de nombre de archivo proporcionado por la opción -f.

A continuación añadimos el código para implementar el modo interactivo:

# interactive mode

if [[ -n $interactive ]]; then
   while true; do
      read -p "Enter name of output file: " filename
      if [[ -e $filename ]]; then
        read -p "'$filename' exists. Overwrite? [y/n/q] > "
        case $REPLY in
           Y|y) break
                ;;
           Q|q) echo "Program terminated."
                exit
                ;;
           *)   continue
                ;;
        esac
      elif [[ -z $filename ]]; then
         continue
      else
         break
      fi
   done
fi

Si la variable interactive no está vacía, se inicia un bucle infinito, que contiene el prompt del nombre del archivo y el consiguiente código existente de manejo de archivos. Si el archivo de salida deseado ya existe, se pregunta al usuario si quiere sobrescribir, elegir otro nombre de archivo o salir del programa. Si el usuario elige sobrescribir un archivo existente, se ejecuta un break para terminar el bucle. Fíjate cómo la sentencia case sólo detecta si el usuario sobrescribe o sale. Cualquier otra opción hace que el bucle continúe y pregunte al usuario de nuevo.

Para implementar la función de nombre de archivo de salida, primero deberíamos convertir el código de escritura de página existente en un función de shell, por las razones que veremos claras en un momento:

write_html_page () {
    cat <<- _EOF_
    <HTML>
        <HEAD>
            <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
            <H1>$TITLE</H1>
            <P>$TIMESTAMP</P>
            $(report_uptime)
            $(report_disk_space)
            $(report_home_space)
        </BODY>
    </HTML>
    _EOF_
    return
}

# output html page

if [[ -n $filename ]]; then
    if touch $filename && [[ -f $filename ]]; then
        write_html_page > $filename
    else
        echo "$PROGNAME: Cannot write file '$filename'" >&2
        exit 1
    fi
else
    write_html_page
fi

El código que maneja la lógica de la opción -f aparece al final de la lista mostrada anteriormente. En él, comprobamos la existencia de un nombre de archivo, y si encuentra uno, se realiza un test para ver si el archivo es modificable. Para hacer esto, se realiza un touch, seguido de un test para determinar si el archivo resultante es un archivo normal. Estos dos tests se encargan de situaciones donde se le indica una ruta no válida (touch fallará), y si el archivo ya existe, que sea un archivo normal.

Como podemos ver, se llama a la función write_html_page para realizar la generación de la página. Su salida es dirigida a la salida estándar (si la variable file-name está vacía) o redirigida a un archivo especificado.

No hay comentarios:

Publicar un comentario