Использование getopt в C с необязательными аргументами

Я делаю небольшую программу на C, которая обрабатывает множество аргументов командной строки, поэтому я решил использовать getopt для их сортировки.

Однако я хочу, чтобы два необязательных аргумента (исходный файл и файл назначения) были обязательными, поэтому вы должны использовать их в качестве аргументов при вызове программы, даже если нет флагов или других аргументов.

Вот упрощенная версия того, что мне нужно для обработки аргументов с помощью флагов:

while ((c = getopt(argc, argv, "i:d:btw:h:s:")) != -1) {
    switch (c) {
        case 'i': {
            i = (int)atol(optarg);
        }
        case 'd': {
            d = (int)atol(optarg);
        }
        case 'b':
            buf = 1;
            break;
        case 't':
            time = 1;
            break;
        case 'w':
            w = (int)atol(optarg);
            break;
        case 'h':
            h = (int)atol(optarg);
            break;
        case 's':
            s = (int)atol(optarg);
            break;
        default:
            break;
    }
}

Как мне отредактировать это так, чтобы аргументы, не являющиеся параметрами, также обрабатывались?

Я также хочу иметь возможность иметь не-опции либо до , либо после опций, так как это будет обрабатываться?


person Conor Taylor    schedule 06.08.2013    source источник


Ответы (3)


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

Добавьте код, подобный этому, после цикла параметров:

if (argv[optind] == NULL || argv[optind + 1] == NULL) {
  printf("Mandatory argument(s) missing\n");
  exit(1);
}

Изменить:

Если вы хотите разрешить параметры после обычных аргументов, вы можете сделать что-то похожее на это:

while (optind < argc) {
  if ((c = getopt(argc, argv, "i:d:btw:h:s:")) != -1) {
    // Option argument
    switch (c) {
        case 'i': {
            i = (int)atol(optarg);
        }
        case 'd': {
            d = (int)atol(optarg);
        }
        case 'b':
            buf = 1;
            break;
        case 't':
            time = 1;
            break;
        case 'w':
            w = (int)atol(optarg);
            break;
        case 'h':
            h = (int)atol(optarg);
            break;
        case 's':
            s = (int)atol(optarg);
            break;
        default:
            break;
    }
    else {
        // Regular argument
        <code to handle the argument>
        optind++;  // Skip to the next argument
    }
}
person Klas Lindbäck    schedule 06.08.2013
comment
Хорошо, но мой цикл завершится, если обязательные аргументы будут перед необязательными, поэтому будут обработаны только обязательные, а не необязательные. Как это исправить? - person Conor Taylor; 07.08.2013
comment
Обычно требуется, чтобы опции стояли перед аргументами. Просто укажите его на странице man. - person Klas Lindbäck; 07.08.2013
comment
Да, я знаю, но, например, с командой ssh ​​флаг -p может стоять перед или после обязательного аргумента username@server. Я просто хочу знать, как это сделать - person Conor Taylor; 07.08.2013
comment
@Conor Я добавил схему того, как это сделать, см. Редактирование выше. - person Klas Lindbäck; 07.08.2013
comment
Ваш код редактирования был очень полезен, спасибо. Немного глупо, что мне приходится принудительно продолжать синтаксический анализ с помощью микроуправления глобальной переменной, но это делает свою работу! - person Evan Senter; 08.08.2014
comment
Следует проверить, что argc > optind перед доступом к argv[optind]! - person gatopeich; 25.07.2017
comment
Стандарт C гарантирует, что @gatopeich argv[argc] будет NULL, поэтому предоставленный мной код должен работать нормально. - person Klas Lindbäck; 26.07.2017
comment
Спасибо, я этого не знал. Тем не менее, строго безопаснее и эффективнее сравнивать argc с optind: if ( optind+2 > argc) { <missing 2 arguments> }. - person gatopeich; 27.07.2017
comment
Пример, который вы предоставили в разделе EDIT, явно не работает (по крайней мере, в Linux), я не могу поверить, что никто не заметил этого с тех пор, как он был впервые опубликован. - person Peter; 07.07.2019

Действительно хороший пример можно найти здесь: GNU Libc Код:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
int aflag = 0;
int bflag = 0;
char *cvalue = NULL;
int index;
int c;

opterr = 0;

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

printf ("aflag = %d, bflag = %d, cvalue = %s\n",
    aflag, bflag, cvalue);

for (index = optind; index < argc; index++)
printf ("Non-option argument %s\n", argv[index]);
return 0;
}

Это позволяет иметь параметры до и после аргументов. Я скомпилировал и запустил тестовый пример:

$ ./a.out aa ff bb -a -ctestparam hello
aflag = 1, bflag = 0, cvalue = testparam
Non-option argument aa
Non-option argument ff
Non-option argument bb
Non-option argument hello
person Madars Vi    schedule 05.06.2017
comment
Это не работает с версией getopt для Mac OSX. - person ishmael; 02.07.2019
comment
в linux glibc optind будет указывать на обязательные аргументы, даже если они находятся за опциями после getopt() - person keniee van; 07.04.2020

Согласно https://www.man7.org/linux/man-pages/man3/getopt.3.html

По умолчанию getopt() переставляет содержимое argv при сканировании, так что в конечном итоге все неопции оказываются в конце. Также реализованы два других режима сканирования. Если первым символом строки опций является '+' или установлена ​​переменная среды POSIXLY_CORRECT, то обработка опций останавливается, как только встречается аргумент, не являющийся опцией. Если первым символом строки опций является '-', то каждый неопционный элемент argv обрабатывается так, как если бы он был аргументом опции с кодом символа 1. (Это используется программами, которые были написаны для ожидания опций и других элементов argv. в любом порядке, и это заботится о порядке этих двух.) Специальный аргумент -- принудительно завершает сканирование опций независимо от режима сканирования.

person Langson.Leung    schedule 24.01.2021
comment
Хотя ваша цитата может информировать о рассматриваемой проблеме, пожалуйста. уточните свой ответ, чтобы ответить на вопрос. Как мне отредактировать это, чтобы также обрабатывались аргументы, не являющиеся параметрами? - person code-kobold; 24.01.2021