Где getopt_long хранит непризнанную опцию?

Когда getopt или getopt_long обнаруживает недопустимый параметр, он сохраняет символ параметра оскорбления в optopt. Когда незаконный вариант является длинным вариантом, где я могу узнать, какой вариант был? И делает ли что-нибудь содержательное в optopt, затем?

Я установил opterr = 0 для подавления автоматически печатаемого сообщения об ошибке. Я хочу создать собственное сообщение, которое я могу распечатать или зарегистрировать там, где захочу, но хочу включить имя непризнанной опции.

Ответ 1

Самое близкое, что я могу найти, это то, что если вы получите BADCH, верните элемент argv, который вызвал его в argv[optind-1]. Похоже, что должен быть лучший способ найти аргумент проблемы.

Ответ 2

Вы совершенно правы, что справочная страница заглатывает эти детали, но из исходного кода можно получить достаточные советы, например, реализацию glibc в glibc-x.y.z/posix/getopt.c _getopt_internal_r. (Возможно, это единственная интересная реализация этой функции расширения GNU?)

Этот код устанавливает optopt в 0, когда он встречает ошибочную длинную опцию, которая, как мне кажется, полезна, чтобы отличить этот случай от ошибочной короткой опции, когда optopt, безусловно, будет не-NUL.

Сообщения об ошибках, возникающие при opterr != 0, в основном распечатывают ошибочный длинный вариант как argv[optind], а более поздний код (всегда или - консервативно - по крайней мере в основном) позже увеличивает optind перед возвратом.

Следовательно, рассмотрим эту программу:

#include <getopt.h>
#include <stdio.h>

int main(int argc, char **argv) {
  struct option longopts[] = {
    { "foo", no_argument, NULL, 'F' },
    { NULL, 0, NULL, 0 }
  };
  int c;

  do {
    int curind = optind;
    c = getopt_long(argc, argv, "f", longopts, NULL);
    switch (c) {
    case 'f':  printf("-f\n");  break;
    case 'F':  printf("--foo\n");  break;
    case '?':
      if (optopt)  printf("bad short opt '%c'\n", optopt);
      else  printf("bad long opt \"%s\"\n", argv[curind]);
      break;
    case -1:
      break;
    default:
      printf("returned %d\n", c);
      break;
    }
  } while (c != -1);

  return 0;
}

$./longopt -f -x --bar --foo
-f
./longopt: неверный параметр - 'x'
плохой short opt ​​'x'
./longopt: непризнанная опция '--bar'
bad long opt "--bar"
--foo

Таким образом, в этих случаях, кэшируя значение pre- getopt_long optind, мы можем легко распечатать те же самые плохие параметры, что и сообщения opterr.

Это может быть не совсем правильно во всех случаях, поскольку использование glibc для использования собственного __nextchar, а не argv[optind] (в случае "непризнанной опции" ) заслуживает изучения, но этого должно быть достаточно, чтобы вы начали.

Если вы внимательно относитесь к взаимосвязи между optind и повторяющимися вызовами getopt_long, я думаю, что распечатка argv[cached_optind] будет довольно безопасной. optopt существует, потому что для коротких опций вам нужно знать, какой именно символ внутри слова является проблемой, но для длинных опций проблема - это все текущее слово (modulo отключение опционных аргументов формы =param). И текущее слово - это то, которое getopt_long смотрит с (входящим) значением optind.

В отсутствие гарантии, написанной в документации, я был бы несколько менее оптимистичным в использовании поведения optopt = 0.