2010-02-08 13 views
17

Ho un programma che accetta vari argomenti della riga di comando. Per motivi di semplificazione, diremo che ci vuole 3 bandiere, -a, -b, e -c, e utilizzare il seguente codice per analizzare i miei argomenti:getopt non riesce a rilevare l'argomento mancante per l'opzione

int c; 
    while((c = getopt(argc, argv, ":a:b:c")) != EOF) 
    { 
     switch (c) 
     { 
      case 'a': 
       cout << optarg << endl; 
       break; 
      case 'b': 
       cout << optarg << endl; 
       break; 
      case ':': 
       cerr << "Missing option." << endl; 
       exit(1); 
       break; 
     } 
    } 

nota: a, e parametri b prendere dopo la bandiera.

Ma mi imbatto in un problema se invoco il mio programma dire con

./myprog -a -b parameterForB 

dove ho dimenticato parameterForA, il parameterForA (rappresentato da OPTARG) viene restituito come -b e parameterForB è considerata un'opzione con nessun parametro e optind è impostato sull'indice di parameterForB in argv.

Il comportamento desiderato in questa situazione sarebbe che ':' viene restituito dopo che nessun argomento è stato trovato per -a e Missing option. viene stampato per errore standard. Tuttavia, ciò si verifica solo nel caso in cui -a sia l'ultimo parametro passato nel programma.

Credo che la domanda sia: esiste un modo per fare in modo che getopt() assuma che nessuna opzione inizi con -?

risposta

11

Vedere la POSIX standard definition per getopt. Si dice che

Se [getopt] rileva una mancanza opzione-argomento, rinvia il carattere due punti (':') se la prima carattere di optstring era un colon o un Question- contrassegno carattere ('?') in caso contrario.

Quanto a che il rilevamento,

  1. Se l'opzione era l'ultimo carattere della stringa puntata da un elemento di argv, poi OPTARG deve contenere l'elemento successivo di argv, e optind deve essere incrementato di 2. Se il valore risultante di optind è maggiore di argc, questo indica un argomento opzionale mancante e getopt() deve restituire un'indicazione di errore.
  2. In caso contrario, OPTARG deve puntare alla stringa che segue il carattere di opzione in quell'elemento di argv, e OPTIND sono incrementati di 1.

Sembra getopt è definito di non fare quello che vuoi, quindi devi implementare il controllo da solo. Fortunatamente, puoi farlo ispezionando *optarg e cambiando optind tu stesso.

int c, prev_ind; 
while(prev_ind = optind, (c = getopt(argc, argv, ":a:b:c")) != EOF) 
{ 
    if (optind == prev_ind + 2 && *optarg == '-') { 
     c = ':'; 
     -- optind; 
    } 
    switch (… 
+0

Questo ha il vantaggio aggiuntivo di consentire effettivamente argomenti che iniziano con '-' - non possono essere preceduti solo da spazi bianchi. (ad es. 'my_prog -x-a') – Potatoswatter

4

Divulgazione completa: non sono esperto in materia.

da gnu.org è utile? Sembra gestire il '?' carattere nei casi in cui un argomento previsto non è stato fornito:

while ((c = getopt (argc, argv, "abc:")) != -1) 
    switch (c) 
    { 
     case 'a': 
     aflag = 1; 
     break; 
     case 'b': 
     bflag = 1; 
     break; 
     case 'c': 
     cvalue = optarg; 
     break; 
     case '?': 
     if (optopt == 'c') 
      fprintf (stderr, "Option -%c requires an argument.\n", optopt); 
     else if (isprint (optopt)) 
      fprintf (stderr, "Unknown option `-%c'.\n", optopt); 
     else 
      fprintf (stderr, 
        "Unknown option character `\\x%x'.\n", 
        optopt); 
     return 1; 
     default: 
     abort(); 
    } 

aggiornamento: Forse il seguente avrebbe funzionato come una correzione?

while((c = getopt(argc, argv, ":a:b:c")) != EOF) 
{ 
    if (optarg[0] == '-') 
    { 
     c = ':'; 
    } 
    switch (c) 
    { 
     ... 
    } 
} 
+1

il ':' all'inizio della mia stringa nella chiamata a getopt() dice che per restituire un ':' nel caso in cui un parametro noto richiede un argomento. Inoltre, ho affermato che "che si verifica nel caso in cui -a sia l'ultimo parametro passato nel programma." Quale è esattamente il caso nel tuo esempio. – finiteloop

+0

Sì, lo hai specificato nella tua domanda. Colpa mia per non aver prestato abbastanza attenzione. Ad ogni modo, ho pubblicato un emendamento con una breve istruzione 'if' che potrebbe fornire il comportamento che stai cercando. Buona fortuna! –

+0

questo sembra una soluzione abbastanza buona. grazie per l'aiuto. – finiteloop

1

ci sono un bel paio di versioni diverse di getopt in giro, quindi, anche se si può ottenere a lavorare per una versione, ci sarà probabilmente almeno cinque altri per i quali la vostra soluzione si romperà. A meno che tu non abbia una ragione schiacciante per usare getopt, prenderei in considerazione qualcos'altro, come ad esempio Boost.Program_options.

2

In alternativa per i progetti senza Boost, ho un semplice colpo di testa-solo involucro C++ per getopt (sotto la BSD 3-Clause licenza): https://github.com/songgao/flags.hh

Tratto da example.cc nel repository:

#include "Flags.hh" 

#include <cstdint> 
#include <iostream> 

int main(int argc, char ** argv) { 
    uint64_t var1; 
    uint32_t var2; 
    int32_t var3; 
    std::string str; 
    bool b, help; 

    Flags flags; 

    flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!"); 
    flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha..."); 
    flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1"); 
    flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1"); 
    flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2"); 

    flags.Bool(help, 'h', "help", "show this help and exit", "Group 3"); 

    if (!flags.Parse(argc, argv)) { 
    flags.PrintHelp(argv[0]); 
    return 1; 
    } else if (help) { 
    flags.PrintHelp(argv[0]); 
    return 0; 
    } 

    std::cout << "var1: " << var1 << std::endl; 
    std::cout << "var2: " << var2 << std::endl; 
    std::cout << "var3: " << var3 << std::endl; 
    std::cout << "str: " << str << std::endl; 
    std::cout << "b: " << (b ? "set" : "unset") << std::endl; 

    return 0; 
} 
Problemi correlati