Bienvenidos sean a este post, hoy veremos una variante interesante de lo visto en el post anterior.
Lo visto en el post anterior es un codigo muy simple de procesador de argumentos de linea de comando donde solamente mostramos en pantalla lo que le pasamos, otra opcion interesante de esto es utilizar las opciones o switches para que nuestro programa haga una tarea especifica, les muestro un ejemplo que hemos estado utilizando:
$ gcc fuente/ejemplo33.c -o prog/ejemplo33
En este caso tenemos nuestro compilador donde primero le pasamos el archivo de origen, luego el switch -o para indicarle que el proximo valor que informemos sera para indicar cual sera el nombre del archivo de salida, si han trabajado con este tipo de terminales o de la vieja escuela como DOS saben que los programas en general tienen muchos de estos switchs para indicarle acciones especificas u opciones para indicar su comportamiento, la mala noticia es que no existe un standar para estos switch y estos seran acordes al criterio del programador y la buena noticia es que no tenemos que reinventar la polvora dado que el lenguaje nos provee de dos funciones en nuestras librerias estandar para manejar las opciones como son getopt y getopt_long.
La funcion getopt es la mas antigua y viene incluida con la libreria unistd.h, en cambio la funcion getopt_long al ser mas moderna viene incluida en la libreria getopt.h, primero analicemos como es la sintaxis de getopt:
getopt(int argc, char* argv[], const char* optstring)
Siendo los dos primeros los valores que recibe el main desde la linea de comandos tal como vimos en el post anterior, la magia viene con el tercer argumento donde estableceremos cuales son las letras que identificaran a nuestras opciones, veamos las posibles opciones que procesa:
- si la opcion toma un valor, esta sera almacenada en optarg
- nos devolvera -1 cuando no haya mas opciones que procesar
- cuando no haya una opcion reconocida nos devolvera un ? y lo almacenara en optopt
- cuando las opciones necesiten un valor y no este presente tambien nos devolvera un ? pero si en optstring utilizamos como primer caracter : nos devolvera este ultimo en lugar del otro cuando no pasemos ningun valor
Con esto tenemos lo basico para trabajar con las opciones, para ello vamos a crear un nuevo archivo que llamaremos ejemplo34.c y le agregaremos el siguiente codigo:
ejemplo34.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int opcion;
while((opcion = getopt(argc, argv, ":if:lrx")) != -1)
{
switch(opcion)
{
case 'i':
case 'l':
case 'r':
printf("Opcion pasada: %c\n", opcion);
break;
case 'f':
printf("Archivo: %s\n", optarg);
break;
case ':':
printf("se necesita un valor\n");
break;
case '?':
printf("Opcion desconocida\n");
break;
}
}
for(;optind < argc; optind++)
{
printf("Argumentos extras: %s\n", argv[optind]);
}
}
Aqui tennemos la curiosidad de que primero utilizamos a una de las librerrias antes mencionadas, unistd.h, como corresponde pasamos a los argumetnos del main, nuestro primer paso sera declarar una variable de tipo int, despues usaremos usaremos un bucle while donde el resultado de getopt lo almacemaremos en la variable amterior, como dijimos debemos pasar todo lo que recibimos desde la linea de comandos, por ultimo tenemos la cadena de opciones, en este caso como mencionamos primero pasamos a «:» para que la falta de valor sea reemplazada con esta, y despues lo volvemos a usar despues de f, en este caso es para indicarle que la opcion «f» debe tener un valor asociado, cada una de las letras que pasamos representa las distintas opciones que podemos utilizar, por ultimo este bucle lo haremos mientras sea distinto de -1, dentro del bucle tenemos un switch que evalua el valor de opcion, las tres primeras simplemente indicaremos que opcion pasamos, despues tenemos el case de f para indicar cual archivo es, este se emcuentra almacenado en optarg, lo siguiente es la opcion de los dos puntos para indicar que nos falto un valor, por ultimo la opcion del signo de interrogacion para indicar que es una opcion invalida, con esto ya tenemos todas las opciones cubiertas, y por ultimo tenemos un bucle for donde devvolveremos cualquier argumento extra que haya pasado a parte de las opciones, observen que para cumplirse tenemos la condicion de que optind, variable interna, sea menor a argc y la incrementaremos, en ella mostraremos cada uno de ellos hasta q se termine el bucle, vamos a compilarlo y ver como funciona mediante el siguiente video
En el video podemos ver las distintas acciones que toma el codigo con las opciones, con una curiosidad particular, en este caso tenemos una opcion que no procesa como es x, si observan la utilizamos pero al no agregar al switch este valor no hace nada pero no nos devuelve un error de invalido como si ocurre con la a, recuerden que mientras la declaremos en getopt siempre sera valida pero queda en nostros procesarla, con todo esto comentado ya tenemos un ejemplo de getopt, ahora pasemos a la otra funcion (getopt_long) y para ello debemos tomar el archivo y cambiaremos el codigo actual por el siguiente:
ejemplo34.c
#include <stdio.h>
#include <getopt.h>
#include <stdbool.h>
static struct option mas_opciones[] = {
{"titulo", required_argument, NULL, 't'},
{"autor", required_argument, NULL, 'a'},
{NULL, 0, NULL, 0}
};
typedef struct _libro{
char* titulo;
char* autor;
} Libro;
int main(int argc, char* argv[])
{
char op;
Libro l;
l.titulo=NULL;
l.autor=NULL;
while( true )
{
op = getopt_long(argc, argv, "t:a:", mas_opciones, NULL);
if (op == -1) break;
switch(op)
{
case 't':
l.titulo = optarg;
break;
case 'a':
l.autor = optarg;
break;
default:
printf("Uso: %s -titulo 'titulo'", argv[0]);
printf("-autor 'nombre'\n");
break;
}
}
if (l.titulo) printf("Titulo: %s\n", l.titulo);
if (l.autor) printf("Autor: %s\n", l.autor);
if (optind == argc)
{
printf("Sin argumentos extras\n");
} else {
for(; optind < argc; optind++)
printf("Argumento extra: %s\n", argv[optind]);
}
}
Este codigo es un poco mas complejo pero no tanto con respecto al anterior, primero importaremos la libreria getopt.h y stdbool.h para nuestras instrucciones, despues tenemos un struct de tipo option, declarado en getopt.h, en este caso lo hacemos static y lo llamaremos mas_opciones, este sera el encargado del agregado de mas opciones para la funcion getopt_long, veamos en detalle del struct:
static struct option mas_opciones[] = {
{"titulo", required_argument, NULL, 't'},
{"autor", required_argument, NULL, 'a'},
{NULL, 0, NULL, 0}
};
Si observan la estructura siempre sera la misma, el primero sera la opcion de titulo larga, el segundo es el valor para indicar si la opcion tiene valor o no, en este caso usamos esta constante para indicar que debemos pasar un valor, en caso de que no reciben ningun valor usaremos no_argument, el tercer valor es para indicar cual sera el valor a devolver, por lo general se deja en NULL porque de esta forma devuelve el valor que establecemos en el siguiente valor, de otra forma devolvera un valor de 0 y apuntara a una variable establecida en el siguiente valor cuando encuentre la opcion de lo contrario no hara nada, el ultimo valor por lo general es representado con la opcion corta de la opcion, en este caso observen que pasamos la primer letra de las versiones largas, como dijimos tambien puede tener una variable que es apuntado por el valor anterior pero en nuestro codigo lo usamos para la version corta, por lo tanto para definir nuestra struct en su forma mas basica siempre sera de esta manera:
struct option {
const char* nombre;
int tiene_arg;
int* flag;
int val;
}
Ahora veremos como lo aplicamos pero por el momento continuemos con el codigo, lo siguiente sera un struct que representara nuestro «libro», observen que va a tener dos campos, uno para el titulo y otro para el autor, los mismos para las opciones de nuestro codigo, de nuevo seguiremos con nuestros argumentos para el main y lo primero que haremos sera definir una variable para recibir la opcion, en este caso usamos un char pero puede ser un int recuerden que es indistinto, la siguiente sera la creacion del objeto basado en el struct Libro, y despues estableceremos el valor NULL para las dos variables del objeto, lo siguiente sera un un bucle infinito por medio de un while, y en ella tendremos esta linea primero:
op = getopt_long(argc, argv, "t:a:", mas_opciones, NULL);
Es exactamente lo mismo que vimos en el otro codigo dado que recibira la opcion y la almacenara en la variable, pero en lugar de usar getopt utilizaremos getopt_long, observen que los tres primeros parametros son similares a getopt, inclusive con los dos puntos dado que tambien simbolizan que esa opcion debe contener un argumento, pero aca viene la primera curiosidad es que pasamos el struct mas_opciones, esto nos permitira utilizar el operador — para agregar las opciones largas, ademas de la normal que utilizamos en el codigo anterior, el ultimo campo si no es NULL apunta a una variable que se establece en el índice de la opción larga relativa a longopts, por lo general se pasara como NULL, al igual que en el caso anterior una vez terminadas todas las opciones nos devolvera un valor de -1, por esta razon despues de la linea anterior tenemos un condicional donde en caso de cumplirse usa un break para salir del bucle infinito, nuestro siguiente paso sera el switch.
Este switch evalua los posibles valores que nos puede devolver getopt_long, observen que dependiendo del valor lo agregaremos al valor del objeto l, y por ultimo un default donde nos comenta que formato debemos utilizar, por fuera del while tenemos dos condicionales donde mostraran los valores del titulo y autor si estos estan establecidos, de lo contrario pasara de largo, para finalmente mediante un condicional verificamos si optind y argc son del mismo valor, en caso de ser verdadero mostrara un mensaje diciendo que no existe ningun valor extra, en caso contrario volveremos a utilizar el mismo bucle que en el codigo anterior, conn todo esto comentado podemos pasar a compilar y ver como trabaja nuestro codigo mediante el siguiente video
Como pueden ver en el video ahora no solamente manejaremos opciones de tipo -t -a sino que tambien podemos usar otras mas explicitas por medio de –autor –titulo, tambien manejamos errores y tecnicamente no son muy distintas salvo el tema del struct para manejar las opciones mas largas pero si no queremos crearlo o no queremos habilitarlas podemos utilizarla como si fuera un getopt, como siempre estas dos funciones siempre seran utilizadas acorde a sus necesidades y/o situaciones, ya sea porque son codigos viejos o codigos mas simples donde no necesitamos tantas opciones, o si.
En resumen, hoy hemos visto como manejar las opciones desde una linea de comandos, primero con la version vieja, como es su sintaxis y luego por medio de un ejemplo como trabaja, despues vimos como es la version mas nueva, como se asemeja a la anteiror pero por medio de un struct podemos ampliar su utilidad, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
