15. Разбор аргументов программы#

Под аргументами командной строки мы подразумеваем данные, передаваемые запускаемой программе из командной строки. Аргументы разделяются друг от друга пробелами или табуляцией. Имя программы составляет первый и обязательный аргумент.

user@host:~$ ls -rhl --all --sort size ~

Вывод на экран файлов и каталогов (команда ls) и скрытого содержимого (опция --all) домашнего каталога (операнд ~) с подробной информацией (опция -l), отсортированных по размеру (опция --sort size) в обартном порядке (опция -r) в удобном для восприятия формате размера (опция -h).

Как видим, аргументы командной строки не совпадают с опциями. Аргумент содежит одновременно 3 опции (-rhl) или только опцию (–sort), но без ее значения (size). Нужна дополнительная обработка данных, которая бы разобрала и извлекла опции, значения опций и операнды программы.

Каждая утилита UNIX начинает работу с разбора аргументов командной строки. Чтобы упростить эту работу и прийти к единому соглашению, было решено реализовать общие функции разбора. Для разбора параметров командной строки воспользуемся существующим решением – функциями getopt и getopt_long и набором глобальных переменных optarg, optind, opterr, optopt. Функции входят в состав ОС UNIX в виде библиотеки Си. Справочная информация man 3 getopt.

Программа на Си начинается с главной функции:

int main(int argc, char** argv)

Функция на вход принимает два аргумента:

  • argc, количество переданных в программу аргументов;

  • argv, список аргументов.

#include <stdio.h>

int main(int argc, char** argv)
{
  int i;
  for(i = 0; i < argc; ++i)
    printf("argv[%d] = %s\n", i, argv[i]);
  return 0;
}

Разбор коротких опций#

Функция getopt разбирает аргументы командной строки на короткие опции. При этом длинные опции она не поддерживает. int getopt(int argc, char* const argv[], const char* optstring) Первые два аргумента функции, argc и argv, определяют количество и массив аргументов командной строки. Они поставляются главной функцией main программы. Третий аргумент optstring задает список всех допустимых опций в виде строки. При каждом вызове функции getopt извлекает из аргументов командной строки следующую опцию и ее значение (если она задана). Если разбор опций заканчивается, то функция возвращает -1. Цикл разбора будет выглядеть как:

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

int main(int argc, char** argv)
{
  while(1)
  {
    int c = getopt(argc, argv, "ab:c::");
    if(c == -1)
      break;
  }
  return 0;
}

Формат optstring следующий. В ней перечислены буквенные символы опций. Если опция принимает значение, то после нее ставят символ :. Если опция принимает значение, но оно необязательно, то ставят два символа – ::. В примере выше определены три опции -a, -b, -c. Опция -a без параметра, опция -b с обязательным параметром, а опция -c может иметь или не иметь параметр. Функция getopt возрвщает символ разобранной опции, а ее значение (если оно задано), заносится в глобальную переменную optarg. Если значение на задано, то optarg=0.

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

int main(int argc, char** argv)
{
  while(1)
  {
    int c = getopt(argc, argv, "ab:c::");
    if(c == -1)
      break;

    switch(c)
    {
    case 'a':
      break;
    case 'b':
      break;
    case 'c':
      break;
    };
  }
  return 0;
}

Если во время разбора встретится опция, которой нет в списке optstring, то функция getopt вернет символ ?, а символ опции заносится в optopt. Сообщение об ошибке разбора выведется в stderr. Чтобы заблокировать вывод сообщения об ошибке, глобальная переменная opterr устанавливается в 0.

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

int main(int argc, char** argv)
{
  opterr = 1;
  while(1)
  {
    int c = getopt(argc, argv, "ab:c::");
    if(c == -1)
      break;
    switch(c)
    {
    case '?':
      fprintf(stderr, "Try \"%s\" with options -a, -bParam or -c (-cParam)\n", argv[0]);
      return 1;
    case 'a':
      break;
    case 'b':
      break;
    case 'c':
      break;
    };
  }
  return 0;
}

При повторном вызове функции getopt нужно знать, где она остановила разбор. Это состояние хранится в глобальной переменной optind – индекс необработанного аргумента в массиве argv. В самом начале ей назначена 1. Если понадобиться разобрать заново аргументы в строке, optind устанавливают в 1. Когда разбор опций заканчивается, т.е. функция getopt возвращает -1, а optind указывает на первый аргумент, который не относится к опциям. За опциями команды следуют параметры команды. Чтобы их извлечь, воспользуемся кодом:

  while(optind < argc)
    printf("operand = %s\n", argv[optind++]);

Разбор длинных опций#

Функция getopt_long разбирает не только короткие, но и длинные опции. Она ведет себя также, как и getopt. int getopt_long(int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex) Первые три параметра аналогичны функции getopt. Остальные два аргумента связаны с длинными опциями. Рассмотрим их подробнее.

Аргумент longopts содержит описание всех допустимых длинных опций – таблицей длинных опций. Это описание задается массивом записей типа option, объявленного в заголовочном файле getopt.h:

struct option
{
  const char* name;
  int has_arg;
  int* flag;
  int val;
};
  • name, имя длинной опции.

  • has_arg, признак присутствия аргумента у опции. Три возможных значения, которые заданы именованными константами: без аргумента (no_argument), с аргументом (required_argument) или с необязательным аргументов (optional_argument).

  • flag, указывает на переменную, которую необходимо проинициализировать при встрече с опцией. Переменная инициализируется значением поля val. Если flag не задан, то функция getopt_long возвращает значение val.

  • val, значение, ассоциированное с опцией.

Последний аргумент массива longopts инициализируется структурой option, заполненной нулями. Аргумент longindex, если он задан, на выходе инициализируется индексом разобранной опции в структуре longopts. Функция возвращает для длинных опций поле val из структуры option или 0, если задано после flag, или символ короктой опции. -1, если разор опций закончился или ? если встретились с неизвестной опцией или с опцией без параметра.

Вопросы для самоконтроля#

  1. Пусть программа program вызвана с тремя опциями -a, -b, -c следующим образом: ./program -abc. Функция getopt вызовется три раза, пока не вернет -1. Но при первом и втором его вызове глобальная переменная optind останется равным 1. Каким образом функция узнает, откуда начать разбор.

  2. Как передать программе один составной аргумент hello world? Тогда как ./program hello world интерпретируется как два аргумента.