2010-02-08 14 views
17

Mam program, który przyjmuje różne argumenty wiersza poleceń. Dla uproszczenia, będziemy powiedzieć, że trwa 3 flagi, -a, -b i -c i użyć następującego kodu do analizowania moje argumenty:getopt nie wykrywa brakującego argumentu dla opcji

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; 
     } 
    } 

Uwaga: i parametry b wykonać po flagi.

Ale napotkasz problem, jeśli przywołuję mój program powiedzieć z

./myprog -a -b parameterForB 

gdzie zapomniałem parameterForA Z parameterForA (reprezentowaną przez OPTARG) jest zwracany jako -b i parameterForB jest traktowany jako opcja bez parametru oraz optind jest ustawione na indeks parametruForB w argv.

Pożądanym zachowaniem w tej sytuacji jest zwrócenie ':' po tym, jak nie znaleziono argumentu dla -a, a Missing option. zostanie wydrukowany jako błąd standardowy. Dzieje się tak tylko w przypadku, gdy -a jest ostatnim parametrem przekazanym do programu.

Domyślam się, że pytanie brzmi: czy istnieje sposób, aby ustawić getopt(), zakładając, że żadne opcje nie zaczną się od -?

Odpowiedz

11

Zobacz POSIX standard definition dla getopt. Mówi, że

Jeśli [getopt] wykrywa brakujące Option argumentu, musi powrócić charakter dwukropek („:”), jeśli pierwszy znakiem łańcucha opcji był okrężnicy lub wątpliwa znak znaku ("?") w przeciwnym razie.

Co do tej detekcji,

  1. Jeśli opcja był ostatni znak w ciągu wskazywanego przez element argv, następnie Optarg będą zawierać kolejny element argv i Optind zostanie inkrementowany o 2. Jeśli otrzymana wartość optind jest większa niż argc, oznacza to brakujący argument opcji, o wartości , a getopt() zwraca komunikat o błędzie.
  2. Inaczej Optarg powinny wskazywać na ciąg po opcji znaku w tym element argv i Optind będzie zwiększany o 1.

to wygląda getopt jest definiowana nie robić to, co chcesz, więc sam musisz zaimplementować sprawdzanie. Na szczęście możesz to zrobić, sprawdzając siebie pod numerem *optarg i zmieniając optind.

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

Ma to dodatkową zaletę polegającą na zezwalaniu na argumenty rozpoczynające się od '-' - po prostu nie mogą być poprzedzone białymi znakami. (np. 'my_prog -x-a') – Potatoswatter

4

Pełne ujawnienie: Nie jestem ekspertem w tej sprawie.

Czy this example z gnu.org będzie użyteczny? Wydaje się, że obsługuje "?" charakter w przypadkach, gdy spodziewane argument nie został dostarczony:

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(); 
    } 

zmiana: Może dodaje będzie działać jak naprawić?

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

":" na początku mojego ciągu w wywołaniu getopt() mówi mu, aby zwrócił ":" w przypadku, gdy znany parametr wymaga argumentu. Ponadto stwierdziłam, że "to występuje w przypadku, gdy -a jest ostatnim parametrem przekazanym do programu." Dokładnie tak jest w twoim przykładzie. – finiteloop

+0

Tak, określiłeś to w swoim pytaniu. Moja wina, że ​​nie zwracam wystarczająco uwagi. W każdym razie, napisałem poprawkę z krótkim stwierdzeniem "jeśli", które może zapewnić zachowanie, którego szukasz. Powodzenia! –

+0

To wydaje się być wystarczająco dobrą poprawką. dzięki za pomoc. – finiteloop

1

Istnieje kilka różnych wersji getopt wokół, więc nawet jeśli można zmusić go do pracy dla jednej z wersji, prawdopodobnie pojawi się co najmniej pięć innych, dla których Twój obejście będzie złamać. Jeśli nie masz przytłaczającego powodu, aby użyć getopt, rozważę coś innego, na przykład Boost.Program_options.

2

Jako alternatywę dla projektów boost-wolny, mam proste tylko nagłówek C++ otoki dla getopt (pod BSD 3-Clause licencji): https://github.com/songgao/flags.hh

zaczerpnięte z example.cc w repo:

#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; 
} 
Powiązane problemy