Publi

Cómo gestionar los parámetros de un programa en C (parte 2: parámetros largos)

photo-1415348667647-9e035495cfb7_r

Hace unos días publiqué Cómo gestionar los parámetros de nuestros programas con getopt en C como una introducción a la gestión de los argumentos desde nuestros programas en C. De esta forma podremos hacer nuestros programas más flexibles aceptando una entrada del usuario en forma de parámetros y así modificar el comportamiento.

Antes de nada: WordPress cuando ve dos guiones seguidos (- y -) lo interpreta como un guión largo. En ocasiones viene bien y no molesta, pero en un post como este sí que perjudica más que beneficia, puedo desactivar el filtro, pero tengo mis dudas de que haya un post en el que lo quiera así. Tendré que estudiar la opción de desactivarlo sólo para un post determinado.

Para ello vimos getopt(), que nos permitía introducir opciones cortas (-v, -a, -j…) incluso con un parámetro asociado, por lo que podíamos empezar a introducir claves y valores en nuestros programas.

Hoy vemos un ejemplo de getopt_long() (también parecido al ejemplo del manual y derivado del anterior de getopt()) con algunos extras curiosos intentando contemplar varias opciones. El programa simula una conexión a un servicio, para ello especificamos host, usuario, contraseña y algunas cosas más, aunque no conecta ni nada, sólo es hipotético.

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
*************************************************************
* @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.3\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, --help \t\t\tPresenta esta ayuda en pantalla.\n");
  fprintf (stderr, "  -v, --verbose\t\t\t\tActiva el modo verbose.\n");
  fprintf (stderr, "  -u usuario, --user=usuario\t\tEspecifica el nombre de usuario.\n");
  fprintf (stderr, "  -p [password], --pass[=password]\tIntroduce la contraseña.\n");
  fprintf (stderr, "  -P puerto, --port=port\t\tEspecifica el puerto de conexión\n");
  fprintf (stderr, "  --output=xml/json\t\t\tEspecifica el tipo de salida que quieres generar\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;
  int oindex;
  char output[10]="none";
  /* variables globales de getopt */
  extern char* optarg;
  extern int optind;
  extern int optopt;
  extern int opterr;
  opterr = 0;
  struct option cli_options[] = {
    {"verbose", 0, &verbose, 64},
    {"user", 1, NULL, 'u'},
    {"pass", 2, NULL, 'p'},
    {"port", 1, NULL, 'P'},
    {"host", 1, NULL, 'h'},
    {"help", 0, NULL, 'H'},
    {"output", 1, NULL, 0},
    {NULL, 0, NULL, 0}
  };
  while ((c = getopt_long (argc, argv, "Hvu:p::P:h:", cli_options, &oindex)) != -1)
    {
      printf ("c = %d(%c) index = %d\n", c, c, oindex);
    switch (c)
      {
      case 0:
    /* Aquí entramos cuando una opción larga no tiene una opción corta
       para procesarse. */

    if (strncmp(cli_options[oindex].name, "output", 10)==0)
      {
        if ( (strncmp(optarg, "json", 10)==0) || (strncmp(optarg, "xml", 10)==0) )
          strncpy(output, optarg, 10);
        else
          {
        fprintf (stderr, "El parámetro output es incorrecto.\n");
        help(argv[0]);
          }
      }
    break;
      case 'v':
    verbose=1;
        break;
      case 'u':
    user = optarg;
        break;
      case 'p':
    if (!optarg)
      pass = pide_password();
    else
      {
        pass = malloc(strnlen(optarg, 128)+1);
        strncpy(pass, optarg, 128);
        /* Llenamos el password de asteriscos para que no sea visible
           con ps, por ejemplo. */

        for (l=0; l<strnlen(optarg, 128); ++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);
  printf ("Output: %s\n", output);

  for (i = optind; i < argc; i++)
    printf ("Ficheros que se enviaran: %s\n", argv[i]);

  printf ("\n\n");
  return 0;
}

En este caso tenemos opciones largas, es decir en lugar de (-v, -h, -p, etc) podemos utilizar ademas (–verbose, –host, –pass, etc). Es más, podemos utilizar los dos tipos, incluso mezclados:

$ ./connect –verbose -h localhost

Incluso podemos tener opciones como (–output) que no tienen su equivalente en opción corta. Es decir, sólo tienen opción larga, y está bien, porque nos podemos quedar sin abecedario para las opciones cortas y seguir necesitando algunas más.

¿Si ya tenemos opciones cortas, con las que se teclea menos, para qué queremos las largas?
Sobre todo, porque son más fáciles de recordar, en el ejemplo, tenemos dos opciones -p (para el password) y -P (para el puerto), pero en opciones largas tenemos (–pass y –port) lo que hace que sea más fácil recordar cada una de ellas, imaginad cuando tengamos un programa con muchas opciones… puede ser muy confuso.
Además, como decía antes, puede que nos quedemos sin letras para las opciones cortas (tenemos mayúsculas, minúsculas y números, pero aún así puede que nos quedemos cortos, en ese caso, tenemos las opciones largas, que al tener más de una letra habrá muchas más combinaciones.

Veo un struct muy raro para definir las opciones, ¿cómo se hace?
El struct donde se definen las opciones largas es el siguiente:

1
2
3
4
5
6
7
8
9
10
  struct option cli_options[] = {
    {"verbose", 0, &verbose, 64},
    {"user", 1, NULL, 'u'},
    {"pass", 2, NULL, 'p'},
    {"port", 1, NULL, 'P'},
    {"host", 1, NULL, 'h'},
    {"help", 0, NULL, 'H'},
    {"output", 1, NULL, 0},
    {NULL, 0, NULL, 0}
  };

Es un array de structs y cada struct tiene cuatro variabes:

  • nombre de la opción
  • ¿tiene argumento? Vale 0 (o no_argument) si no tiene argumento, es decir, si no queremos darle un valor a esa opción. Valdrá 1 (o required_argument) si es necesario ese argumento, como cuando en las opciones cortas ponemos dos puntos (:), o 2 (o optional_argument) si podemos incluir el argumento o no, vamos que sea opcional, como cuando en las opciones cortas ponemos dos signos de dos puntos (::). Para este último caso, el argumento deberá ser introducido desde línea de comandos con el signo igual:

    $ ./connect –pass=miContraseña

  • La tercera variable:
    • Si vale 0 (o NULL), getopt_long() devolverá el valor de la cuarta variable. Por ejemplo, para «output», estoy devolviendo 0, lo que significa que cuando vea ese argumento en la línea de comandos, dentro del switch, lo procesaré con la opción 0. Pero para «host», estoy devolviendo directamente ‘h’, por lo que haré como si me hubiera entrado la opción corta ‘h’ y procesaré tanto –host como -h de la misma forma.
    • Si tiene otro valor, deberá ser una dirección de memoria. Y en esa dirección de memoria se escribirá el valor de la cuarta variable. Como en el ejemplo, tenemos (&verbose), es decir, metemos la dirección de memoria de esa variable y luego un valor en la cuarta variable. De esta forma, cuando hagamos:

      $ ./connect –verbose

      a la variable verbose (de tipo int) le daremos el valor 64. Es un ejemplo, en este caso para distinguir entre –verbose y -v.

  • La cuarta variable está explicada junto con la tercera

Como con getopt() el tema de opciones obligatorias o control de errores en los argumentos, es decir, que una opción reciba un valor no válido, etc debemos controlarlo nosotros. Por ejemplo, –output sólo acepta json o xml. En este caso he decidido comprobarlo con strncmp(), con una n en medio, igual que strncpy() y similares, para que el programa tenga cierta tolerancia a fallos, valores muy grandes que no caben en nuestras variables, etc.

Aquí he querido incluir varios tipos de opciones
Foto: Maya Karmon

También podría interesarte....

There are 14 comments left Ir a comentario

  1. Pingback: Cómo gestionar los parámetros de un programa en C (parte 2: parámetros largos) | PlanetaLibre /

  2. Pingback: Procesar argumentos de entrada en nuestros shell scripts con getopt – Poesía Binaria /

  3. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Usual visitors here’s the easiest way to thank you so much in your effort and hard work, which unfortunately is the reason why I will be touring the location day to day, in need of different, appealing advice. Countless, bless you! ufabet7777

  4. ghori92 /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Fine Posting, We’re an important believer around writing commentary for web pages so that you can allow the site freelancers realise that they’ve increased a little something valuable so that you can the ether! транспорт на автомобил от германия

  5. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Navigating because of the considerable garden in web based subject matter, you commonly stumbles for an important jewel: a general Web log. The application functions to provide a online digital oasis, offering up unique topic and even perspectives. Regardless if diving inside diet and lifestyle, travelling, and concept, All round Blogging can be tremendous origins, enriching much of our web based go through utilizing wide-ranging and even insightful subject matter. Yohandry.com

  6. seooo servicesss /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Please continue this great work and I look forward to more of your awesome blog posts. forex robot

  7. seooo servicesss /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    it was a wonderful chance to visit this kind of site and I am happy to know. thank you so much for giving us a chance to have this opportunity.. forex robot

  8. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Wow, cool post. I’d like to write like this too – taking time and real hard work to make a great article… but I put things off too much and never seem to get started. Thanks though. forex robot

  9. MUZAMMIL KHATRI /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    Wow, cool post. I’d like to write like this too – taking time and real hard work to make a great article… but I put things off too much and never seem to get started. Thanks though. forex robot

  10. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    This particular papers fabulous, and My spouse and i enjoy each of the perform that you have placed into this. I’m sure that you will be making a really useful place. I has been additionally pleased. Good perform! forex robot

  11. ALEXENDER RAVI /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    Join the fun at AU8 Entertainment City, where every player is treated like royalty! Our dedicated customer service team is available 24/7 to address any concerns, ensuring a seamless gaming experience. Explore our exciting games and start winning today! AU8

  12. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    Win easily by mastering the basics of real money Baccarat online and maintaining a clear mindset. baccarat online

  13. ALEXENDER RAVI /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    Thank you so much for such a well-written article. It’s full of insightful information. Your point of view is the best among many without fail.For certain, It is one of the best blogs in my opinion. forex robot

  14. Karter29 Harold /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    This particular papers fabulous, and My spouse and i enjoy each of the perform that you have placed into this. I’m sure that you will be making a really useful place. I has been additionally pleased. Good perform! forex robot

Leave a Reply