Pull to refresh

Разбор опций командной строки в UNIX-подобных системах

Reading time 7 min
Views 38K

Введение


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

POSIX


Стандарт POSIX описывает ожидаемое поведение программ в UNIX-подобных системах. При написание программы никто вас не заставит строго следовать стандарту, однако, это является хорошей идеей, так как это облегчит жизнь вашим пользователям. Вот основные правила касающиеся аргументов команды:
  • в имени программы должно быть не менее 2 и не более 9 символов;
  • имена программ должны быть написаны только строчными символами и цифрами;
  • имя опции должно быть простым буквенно-цифровым символом. Опции с множеством цифр запрещены;
  • все опции должны начинаться с символа "-";
  • для опций без аргументов должны быть реализована возможность объединения опций (например foo -a -b -c и foo -abc);
  • аргумент опции должен отделятся от нее пробелом;
  • аргумент опции не может быть необязательным;
  • если опции требуется множество значений аргумента, они должны передаваться в виде строки, разделенные запятыми или разделителем;
  • опции должны идти перед операндами;
  • аргумент "--" указывает на окончание всех опций;
  • порядок опций не должен играть роли, кроме случаев когда опции взаимоисключающие, тогда побеждает последняя;
  • порядок аргументов может иметь значение;
  • программы читающие или записывающие именованные файлы, должны трактовать единственный аргумент "-" как стандартный ввод или стандартный вывод соответственно.


Длинные опции


В GNU программах также используются длинные опции, поведение которых не описано в POSIX, длинные опции начинаются с "--" Для этих опций в GNU также реализованы следующие соглашения:
  • каждая короткая опция должна иметь свой вариант длинной опции;
  • длинную опцию можно сократить до кратчайшей строки, обеспечивающей ее уникальность;
  • Аргументы длинной опции отделяются либо разделителем, либо знаком "=".


Откуда берутся параметры в программе


как известно функция main() в С определяется так:
int main(int argc, char *argv[])
Здесь присутствует два параметра: argc определяет количество аргументов в командной строке, а argv хранит массив указателей на эти аргументы.
Следует отметить, что argv[0] — всегда имя команды, а argv[argc] == NULL, эти два факта могут оказаться полезными при разработке.

Разбор опций


В 80-х годах группа поддержки Unix заметила, что каждая программа Unix использует собственные методы разбора опций. Что послужило толчком к разработке функции getopt(), чтобы упростить написание кода, придерживающегося стандартных соглашений.
Функция GNU getopt_long(), является совместимой с getopt(), а также упрощает разбор длинных опций.

getopt


Объявление:
#include <unistd.h>
int getopt(int argc, char *argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;

Аргументы argc и argv передаются непосредственно от функции main(), а optstring является строкой символов опций. Если за какой либо буквой в строке следует двоеточие, значит эта опция принимает аргумент.
Для использования getopt() ее вызывают повторно в цикле, до тех пор, пока она не вернет -1. Каждый раз обнаружив действительный символ опции, функция возвращает этот символ. Если опция принимает аргумент, то указатель на него помещается в переменную optarg.
переменная optind хранит текущий индекс в argv. Когда переменная opterr не равна нулю (по умолчанию 0), getopt() сама выводит сообщения в случае недействительной опции или отсутствия аргумента. Если же opterr равен нулю, то в случае возникновения ошибки getopt() возвращает "?" или ":" в зависимости от того найдена недействительная опция или пропущен обязательный аргумент опции, в переменной optopt будет находится обнаруженный недействительный символ.
Следует заметить, что стандартная функция getopt() останавливается сразу как только найдет первый аргумент начинающийся не с символа "-", GNU вариант функции просматривает в поисках опций, всю командную строку. Поведение GNU функции можно изменить (но это выходит за рамки статьи).

пример программы с использованием getopt()


Любезно предоставил iv_s

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4.  
  5. int main(int argc, char **argv) {
  6.     if(argc == 1) { // если запускаем без аргументов, выводим справку
  7.         printf(«getopt test\n»);
  8.         printf(«usage:\n»);
  9.         printf(" opts -a n -b m -o s\n");
  10.         printf(«example:\n»);
  11.         printf(" $ opts -a 323 -b 23 -o '-'\n");
  12.         printf(" 323 — 23 = 300\n");
  13.         return 0;
  14.     }
  15.     char *opts = «a:b:o:»; // доступные опции, каждая принимает аргумент
  16.     int a, b; // тут храним числа
  17.     char op; // а тут оператор
  18.     int opt; // каждая следующая опция попадает сюда
  19.     while((opt = getopt(argc, argv, opts)) != -1) { // вызываем getopt пока она не вернет -1
  20.         switch(opt) {
  21.             case 'a': // если опция -a, преобразуем строку с аргументом в число
  22.                 a = atoi(optarg);
  23.                 break;
  24.              case 'b': // тоже для -b
  25.                  b = atoi(optarg);
  26.                  break;
  27.              case 'o': // в op сохраняем оператор
  28.                  op = optarg[0];
  29.                  break;
  30.         }
  31.     }
  32.     switch(op) {
  33.         case '+': // если опаратор + складываем, и т.д.
  34.             printf("%d + %d = %d\n", a, b, a + b);
  35.             break;
  36.         case '-':
  37.             printf("%d — %d = %d\n", a, b, a — b);
  38.             break;
  39.         case '*':
  40.             printf("%d * %d = %d\n", a, b, a * b);
  41.             break;
  42.         case '/':
  43.             printf("%d / %d = %d\n", a, b, a / b);
  44.             break;
  45.     }
  46.     return 0;
  47. }
* This source code was highlighted with Source Code Highlighter.


getopt_long()


Объявление:
#include <getopt.h>

int getopt_long(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex);

первые три аргумента те же, что и в getopt(), longopts является указателем на массив длинных опций, longindex указывает на переменную, в которую помещается индекс обнаруженной длинной опции в longopts, если в это нет необходимости может быть NULL.

Структура option определена следующим образом:
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
}


name — имя опции без предшествующих черточек;
has_arg — как понятно из названия, переменная описывает имеет ли длинная опция аргумент, может принимать три значения:
  • 0 — не принимает аргумент;
  • 1 — обязательный аргумент;
  • 2 — необязательный аргумент.

flag — если этот указатель равен NULL, то getopt_long() возвращает значение поля val, иначе она возвращает 0, а переменная на которую указывает flag заполняется значением val;
val — обычно содержит некоторую символьную константу, если длинная опция соответствует короткой, то эта константа должна быть такой же как и та что появляется в аргументе optstring.
Важно заметить, что последний элемент массива longopts, должен быть заполнен нулями.

Пример программы с использованием getopt_long()


Любезно предоставил shuffle

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <getopt.h>
  5.  
  6. void usage(char *name)
  7. {
  8. printf(«usage: %s\n \     \t-h this message\n \     \t-c [config file]\n \     \t--help this message\n \     \t--config=config_file\n», name);
  9. return;
  10. }
  11.  
  12. int main (int argc, char *argv[])
  13. {
  14. int c;
  15. while (1){
  16.  static struct option long_opt[] = {
  17.                     {«help», 0, 0, 'h'},
  18.                     {«config», 1, 0, 'c'},
  19.                     {0,0,0,0}
  20.                   };
  21.  int optIdx;
  22.  
  23.  if((c = getopt_long(argc, argv, «c:h», long_opt, &optIdx)) == -1)
  24.   break;
  25.  switch( c ){
  26.      case 'h':
  27.          usage(argv[0]);
  28.          return(-1);
  29.     
  30.      case 'c':
  31.          printf(«option 'c' selected, filename: %s\n», optarg);
  32.          return(1);
  33.          
  34.      default:
  35.          usage(argv[0]);
  36.          return(-1);
  37.   }
  38. }
  39.  
  40. return(0);
  41. }
* This source code was highlighted with Source Code Highlighter.


Заключение


В статье рассмотрены основы использования функций разбора аргументов командной строки в UNIX-подобных системах. Этот материал может быть более обширен, но любой заинтересовавшийся человек в состоянии узнать и изучить все тонкости самостоятельно.

Статья подготовлена по материалам книги Арнольда Роббинса «Linux программирование в примерах» ISBN 5-9579-0059-1

Tags:
Hubs:
+66
Comments 34
Comments Comments 34

Articles