Как заставить getopt пройти по всему argv

У меня есть цикл, в котором с помощью getopt считываются флаги для grep и, когда вылавливает нужный, говорит структуре, что флаг был введен

    int rez = 0;
    opterr = 0;
    char ** for_patterns = malloc(sizeof(char *));
    while ((rez = getopt(argc, argv, "eivnlc")) != -1) {
        switch (rez) {
            case 'e':
            info->flag_e = true;
            find_patterns(argc, argv, for_patterns);
            break; case 'i':
            info->flag_i = true; 
            break; case 'l':
            info->flag_l = true; 
            break; case 'v':
            info->flag_v = true;
            break; case 'c':
            info->flag_c = true;
            break; case 'n':
            info->flag_n = true;
            break;
            default:
            printf("жесть"); return false;
        }
    }

Но возможны же и ситуации по типу grep -e include int 1.txt -i. В таких случаях гетоп сдается и не видит -i. Как это можно исправить?


Ответы (1 шт):

Автор решения: avp

При использовании getopt "по умолчанию" ключ -i в конце списка аргументов должен попадать в цикл разбора.

Такое впечатление, что у вас либо определена environment переменная POSIXLY_CORRECT, либо какая-то нестандартная реализация getopt
(кстати, в какой ОС вы пишете? а не меняет ли функция find_patterns() содержимое argv?)

Как описано в man getopt, эта функция по умолчанию переставляет элементы argv[] таким образом, что в итоге nonoption аргументы оказываются в конце и переменная optind после цикла разбора содержит индекс первого из них.

Возможно, что в вашем коде за ключем -e должен следовать его аргумент. Если это так, то в строке ключей, передаваемой в getopt, после e нужно поставить символ :. Тогда при обнаружении -e в цикле разбора аргументов переменная optarg будет указывать на этот аргумент.

Для демонстрации сказанного, на основе вашего кода я набросал пример (предполагающий наличие аргумента у ключа -e), который просто распечатывает найденные ключи и "свободные" аргументы (после разбора ключей).

int
main (int ac, char *av[])
{
  int rez = 0;
  opterr = 0;
  //char ** for_patterns = malloc(sizeof(char *));

  while ((rez = getopt(ac, av, "e:ivnlc")) != -1) {
    switch (rez) {
    case 'e':
      puts("info->flag_e = true;");
      printf("-e arg = '%s'\n", optarg);
      //      find_patterns(argc, argv, for_patterns);
      break;
    case 'i':
      puts("info->flag_i = true; ");
      break;
    case 'l':
      puts("info->flag_l = true; ");
      break;
    case 'v':
      puts("info->flag_v = true;");
      break;
    case 'c':
      puts("info->flag_c = true;");
      break;
    case 'n':
      puts("info->flag_n = true;");
      break;
    default:
      printf("жесть");
      //return false;
      return -1;
    }
  }

  printf("flags processed, optind = %d\n", optind);
  if (optind < ac) {
    puts("free args:");
    for (int i = optind; i < ac; i++)
      puts(av[i]);
  }
  
  return puts("End") == EOF;
}

и сделал пару запусков с интересующими вас аргументами.

avp@avp-desktop:~/avp/hashcode$ gcc ttt.c
avp@avp-desktop:~/avp/hashcode$ POSIXLY_CORRECT=1 ./a.out -e include int 1.txt -i
info->flag_e = true;
-e arg = 'include'
flags processed, optind = 3
free args:
int
1.txt
-i
End
avp@avp-desktop:~/avp/hashcode$ ./a.out -e include int 1.txt -i
info->flag_e = true;
-e arg = 'include'
info->flag_i = true; 
flags processed, optind = 4
free args:
int
1.txt
End

На всякий случай, если ключ (option) повторяется, то повторно он также доступен:

avp@avp-desktop:~/avp/hashcode$ ./a.out -e include int 1.txt -i -e '*.c'
info->flag_e = true;
-e arg = 'include'
info->flag_i = true; 
info->flag_e = true;
-e arg = '*.c'
flags processed, optind = 6
free args:
int
1.txt
End
avp@avp-desktop:~/avp/hashcode$ 

P.S.

Хорошее описание getopt находится в GNU C Library Reference Manual
Также там есть глава Program Arguments с подробным описанием правил использования аргументов командной строки и нескольких средств для их разбора.

→ Ссылка