Cuando hablamos de argumentos o parámetros de un programa en C, debemos pensa en los parámetros tan raros que pasamos a la función main(). En este caso, podemos ver cómo con dos argumentos (argc, o el número de argumentos que tenemos y argv o el contenido de esos argumentos, somos capaces de gestionar la información que un usuario nos pasa justo cuando ejecuta nuestro programa.
Tabla de contenidos
Lo más sencillo que podemos hacer con los argumentos
Imaginemos este ejemplo, de un programa donde queremos copiar un archivo origen a un destino (como cp), primero comprobamos que el número de argumentos es suficiente y luego imprimimos en pantalla el valor de cada uno (es un ejemplo inofensivo, no vamos a copiar nada). Tenemos que tener en cuenta que argv[0] encerrará el propio nombre del ejecutable del programa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
A este programa debemos llamarle de la siguiente manera:
Si tengo esto, ¿para qué quiero getopt?
Las opciones que nos da utilizar a pelo son algo limitadas, es decir, si tienes experiencia en la línea de comandos, verás que muchos programas tienen argumentos más flexibles, es decir, tenemos argumentos que activarán algo en el programa, por ejemplo:
$ ls -l
que listará archivos en modo lista, o
$ du -h
que expresará los tamaños en formato humano (Kb, Mb, Gb,…)
Otros argumentos irán acompañados de un valor, como por ejemplo:
$ gcc -o args args.c
para compilar un archivo, tenemos que especificar el archivo salida (output) con -o delante, o:
$ wmctrl -s 3
para saltar al tercer escritorio desde nuestra línea de comandos (requiere wmctrl instalado).
También nos podríamos encontrar el caso de un argumento que reciba un argumento de forma voluntaria como el argumento -p del cliente mysql, por lo que estas dos opciones son válidas:
$ mysql -p
y
$ mysql -p clave
Si usamos la primera forma, nos preguntará la contraseña por teclado y será invisible en pantalla, si usamos la segunda, debemos pasar la contraseña y ésta será visible.
Ni que decir tiene que un buen gestor de parámetros nos debe permitir introducir múltiples argumentos, de diferente índole, y en muchos casos, sin importar el orden de éstos, y por ejemplo usarlos todos juntos, por ejemplo como hace cp:
$ cp -rvpfi origen destino
que nos evita tener que escribir:
$ cp -r -v -p -f -i origen destino
Un pequeño ejemplo de getopt()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /** ************************************************************* * @file connect.c * @brief Input argument management example with getopt * * @author Gaspar Fernández <blakeyed@totaki.com> * @version * *************************************************************/ #include <stdlib.h> #include <stdio.h> #include <getopt.h> #include <ctype.h> #include <string.h> #include <termios.h> void help(char *programa) { fprintf (stderr, "Ejemplo getopt Ver 0.2\nCopyright (c) 2015 xxxx\n\n"); fprintf (stderr, "Este programa hace como que va a conectar con un servidor externo\n" "y tienes que pasarle algunos argumentos para especificar la conexión.\n"); fprintf (stderr, "Uso %s -h host [OPCIONES] [ficheros]\n", programa); fprintf (stderr, " -?, -H\t\tPresenta esta ayuda en pantalla.\n"); fprintf (stderr, " -v\t\tActiva el modo verbose.\n"); fprintf (stderr, " -u usuario\t\tEspecifica el nombre de usuario.\n"); fprintf (stderr, " -p [password]\t\tIntroduce la contraseña.\n"); fprintf (stderr, " -P puerto\t\tEspecifica el puerto de conexión\n\n"); exit (2); } char* pide_password() { struct termios t, ct; char* temp = (char*)malloc(100*sizeof(char)); printf ("Introduce tu clave: "); tcgetattr(0, &t); ct=t; t.c_lflag |= ~ECHO; tcsetattr(0, TCSANOW, &t); scanf("%s", temp); tcsetattr(0, TCSANOW, &ct); return temp; } int main(int argc, char *argv[]) { int verbose = 0; /* flag */ char *user=NULL, *pass=NULL, *host=NULL; int port = 0; int i; int l; int c; /* variables globales de getopt */ extern char* optarg; extern int optind; extern int optopt; extern int opterr; opterr = 0; while ((c = getopt (argc, argv, "vu:p::P:h:")) != -1) switch (c) { case 'v': verbose=1; break; case 'u': user = optarg; break; case 'p': if (!optarg) pass = pide_password(); else { pass = malloc(strlen(optarg)); strcpy(pass, optarg); /* Llenamos el password de asteriscos para que no sea visible con ps, por ejemplo. */ for (l=0; l<strlen(optarg); ++l) optarg[l]='*'; } break; case 'P': port = atoi(optarg); break; case 'h': host = optarg; break; case 'H': help(argv[0]); break; case '?': if (strchr("uPh", optopt) != NULL) fprintf (stderr, "La opción -%c requiere un argumento.\n", optopt); else if (optopt=='?') help(argv[0]); else if (isprint (optopt)) fprintf (stderr, "Opción desconocida'-%c'.\n", optopt); else fprintf (stderr, "Carácter no válido '\\x%x'.\n", optopt); help(argv[0]); default: exit(1); } /* Comprobamos los argumentos obligatorios */ if (!host) { fprintf (stderr, "Debe especificar el host de forma obligatoria!!\n"); help(argv[0]); } if (host) printf ("Host: %s\n", host); if (port) printf ("Port: %d\n", port); if (user) printf ("User: %s\n", user); if (pass) printf ("Pass: %s\n", pass); printf ("Verb: %d\n", verbose); for (i = optind; i < argc; i++) printf ("Ficheros que se enviaran %s\n", argv[i]); printf ("\n\n"); return 0; } |
El código se parece un poco al ejemplo del manual de getopt(), pero con algún que otro extra (ya sabéis que me gusta poner un toque personal a estas cosas). Lo importante que getopt() está en un bucle, y recibe como argumento argv, argc y un listado de argumentos. En dicho listado veréis letras con las diferentes opciones, algunas acompañadas de dos puntos (:) y otras de dos dos puntos (::), el primer caso es para los parámetros que necesitan un argumento (-o salida) y el segundo para los que el argumento es voluntario (-p / -pclave).
Tenemos, de forma especial, el parámetro -v (verbose), que modifica la variable verbose para ponerla a uno. Se dice que es un argumento que acciona un flag, ya que lo único que hace es activar una opción.
Los argumentos obligatorios debemos controlarlos nosotros, podríamos utilizar variables, tal y como hacemos con los flags para especificar los argumentos obligatorios que se han especificado, o incluso utilizar operaciones de bit sobre un entero para hacerlo de manera más rápida y ocupar menos memoria.
Por último, tenemos que saber que getopt() recoloca aquellos argumentos que no están asociados a ninguna opción (-p, -P, -u, -h) y los pone todos al final, de manera que podamos utilizar el último bucle:
1 2 |
para recorrer todos los argumentos huérfanos. Podemos basarnos, como ejemplo, en cp, en el que podemos especificar muchos archivos de origen y finalmente un directorio de destino.
Un caso especial
¿Os habéis preguntado alguna vez qué pasa cuando queremos copiar, eliminar, mover, o lo que sea, un archivo que empieza por – (guión) ?
Si lo pensáis, los parámetros, normalmente también empiezan con el mismo guión, ¿cómo puede saber cp, por ejemplo, qué es parámetro y qué es el archivo?
Para eso, tenemos un parámetro especial, –– (dos guiones, WordPress muchas veces me traiciona y pone un guión muy largo), si ponemos dos guiones delante del nombre de archivo podremos escribirlo sin problema. Por ejemplo:
$ rm –– –archivo_que_empieza_por_guion
Pues bien, sólo debemos saber que getopt() también se encarga de gestionar esto.
Próxima entrega
Podemos hacer más cosas, esto es sólo una introducción, estad atentos a la página de Facebook, o Twitter, o entrad en unos días para ver más novedades.
Foto: Tom Eversley (isorepublic)
Pingback: Cómo gestionar los parámetros de nuestros programas con getopt en C | PlanetaLibre /
Pingback: Cómo gestionar los parámetros de un programa en C (parte 2: parámetros largos) – Poesía Binaria /
Pingback: Procesar argumentos de entrada en nuestros shell scripts con getopt – Poesía Binaria /
Your blog provided us with valuable information. I am looking forward to read more blog posts from here keep it up!!