Базовая обработка командной строки

Соглашения POSIX

Стандарт POSIX описывает ряд соглашений, которых придерживаются удовлетворяющие стандарту программы. Никто не требует, чтобы программы удовлетворяли этим стандартам, но это является хорошим тоном в программировании: пользователи Linux и UNIX всему миру понимают и используют эти соглашения. Более того, функции, с которыми мы познакомимся далее ( getopt ( ) и getopt_long( )), освобождают программиста от бремени ручного разбора аргументов для каждой программы, которую он напишет. Вот эти правила, перефразированные из стандарта:

 

  1. В имени программы должно быть не менее двух и не более девяти символов.
  2. Имена программ должны содержать лишь строчные символы и цифры.
  3. Имя опции должно быть простым буквенно-цифровым символом. Опции с множеством цифр не должны допускаться. Для производителей, реализующих утилиты POSIX, опция -w зарезервирована для специфичных для производителя опций.
  4. Все опции должны начинаться с символа '-'.
  5. Для опций, не требующих аргументов, должно быть возможно объединение нескольких опций после единственного символа '-'. (Например, 'foo -a -b -с' и 'foo -abc' должны интерпретироваться одинаково.)
  6. Когда опции все же требуется аргумент, он должен быть отделен от опции пробелом (например, 'fgrep -f patf ile'). Однако стандарт допускает историческую практику, при которой иногда опция и ее операнд могут находиться в одной строке: 'fgrep -fpatfile'. На практике функции getopt(3) ngetopt_long(3) интерпретируют '-fpatfile' как '-f patfile', а не как'-f -p -a -t . . . .

7. Аргументы опций не должны быть необязательными.Это означает, что если в документации программы указано, что опции требуется аргумент, этот аргумент должен присутствовать всегда, иначе программа потерпит неудачу. GNU getopt(3) все же предусматривает необязательные аргументы опций, поскольку иногда они полезны.

8. Если опция принимает аргумент, который может иметь несколько значений, программа должна получать этот аргумент в виде одной строки со значениями, разделенными запятыми или разделителем.

Например, предположим, что гипотетической программе myprog требуется список пользователей для опции -u. Далее она может быть вызвана одним из двух способов:

myprog -u "arnold, joe, jane" /* разделение запятыми */

myprog -u "arnold joe jane" /* разделение пробелами */

В таком случае вы должны самостоятельно отделить и обработать каждое значение(т.е. здесь нет стандартной процедуры), но ручная реализация обычно проста.

9. Опции должны находиться в командной строке первыми, перед операндами. Версии getopt(3) UNIX проводят в жизнь это соглашение. GNU getopt (3) по умолчанию этого не делает, хотя можно настроить его на это.

10. Специальный аргумент '--' указывает на окончание всех опций. Все последующие аргументы командной строки рассматриваются как операнды, даже если они начи­наются с черточки.

11. Порядок, в котором приведены опции, не должен играть роли. Однако, для взаимно исключающих опций, когда одна опция перекрывает установки другой, тогда (так сказать) последняя побеждает. Если опция, имеющая аргумент, повторяется, программа должна обработать аргументы по порядку. Например, 'myprog -u arnold -u jane' to же самое, что и 'myprog -u "arnold, jane"'. (программисту придется осуществить это самостоятельно; getopt( ) не поможет.)

12. Нормально, когда порядок аргументов имеет для программы значение. Каждая про­грамма должна документировать такие вещи.

13. Программы, читающие или записывающие именованные файлы, должны трактовать единственный аргумент ‘-’ как означающий стандартный ввод или стандартный вывод, в зависимости от того, что подходит программе.

Многие стандартные программы не следуют всем указанным соглашениям. Главной причиной является историческая совместимость; многие такие программы предшествовали систематизации этих соглашений.

 

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

Программам GNU рекомендуется использовать длинные опции в форме --help, --verbose и т. д. Такие опции, поскольку они начинаются с '--', не конфликтуют с соглашениями POSIX. Их также легче запомнить (знающим англ. язык), и они предоставляют возможность последовательности среди всех плит GNU. (Например, --help является везде одним и тем же, в отличие от -h для «help», -i для «information» и т. д.) Длинные опции GNU имеют свои собственные соглашения, реализованные в функции getopc_long(3):

1. У программ, реализующих инструменты POSIX, каждая короткая опция (один символ) должна иметь также свой вариант в виде длинной опции.

2. Дополнительные специфические для GNU опции не нуждаются в соответствующей короткой опции, но мы рекомендуем это сделать.

3. Длинную опцию можно сократить до кратчайшей строки, которая остается уникальной. Например, если есть две опции --verbose и --verbatim, самыми короткими сокращениями будут --verbo и --verba.

4. Аргументы опции отделяются от длинных опций либо разделителем, либо символом = . Например, --sourcefile=/some/file или --sourcefile /some/file.

5. Опции и аргументы могут быть заинтересованы в операндах командной строки; getopc_long( ) переставляет аргументы таким образом, что сначала обрабатываются все опции, а затем все операнды доступны последовательно. (Такое поведение можно запретить.)

6. Аргументы опции могут быть необязательными. Для таких опций считается, что аргу­мент присутствует, если он находится в одной строке с опцией. Это работает лишь для коротких опций. Например, если -х такая опция и дана строка ' foo -xRUSSIANS -z' аргументом -х является 'RUSSIANS'. Для 'fоо -х -z' у -х нет аргументов.

7. Программы могут разрешить длинным опциям начинаться с одной черточки. (Это типично для многих программ X Window.)

GNU Coding Standards уделяет значительное место перечислению всех длинных и коротких опций, используемых программами GNU. Если вы пишете программу, использующую длинные опции, посмотрите, нет ли уже использующихся имен опций, которые имело бы смысл использовать и вам.

 

Базовая обработка командной строки

Программа на Си получает доступ к своим аргументам командной строки через пара­метры argc и argv. Вы можете дать этим переменным любые другие имена! Хотя лучше этого не делать, т.к. код Вашей программы могут читать другие люди, а имена argc и argv понятны всем. Параметр argc является целым, указывающим число имеющихся аргументов (argument count), включая имя команды. Параметр argv (argument variables) является указателем на массив строк, разделенных пробелом. Каждая строка – это отдельный аргумент. Есть два обычных способа определения main( ), отличающихся способом объявления argc:

int main(int argc, char *argv[ ]) или int main(int argc, char **argv)

 

Практически между двумя этими объявлениями нет разницы, хотя первое концептуально более понятно: argc является массивом указателей на символы. А второе определение тех­нически более корректно, это то, что показано на рис. 2 изображена эта ситуация:

 

char ** char *

 
 


“cat” ‘\0’

“file1” ‘\0’

“file2” ‘\0’

 

 

Рис. 2 Память для argc

 

По соглашению, argv[0] является именем программы. Последующие элементы argv[1], argv[2] и т.д. являются аргументами командной строки. Последним элементом массива argv является указатель NULL.

Параметр argc указывает, сколько имеется аргументов; поскольку в Си индексы отсчитываются с нуля,выражение 'argv [argc] == NULL' всегда верно. Из-за этого, особенно в коде для UNIX,можно увидеть различные способы проверки окончания списка аргументов, такие, как цикл с проверкой, что счетчик превысил argc, или 'argv[i] == 0', или '*argv != NULL' и т.д. Онивсе эквивалентны.

 

 

Программа echo V7 (из седьмой версии UNIX System V)

Возможно, простейшим примером обработки командной строки является программа V7 echo,печатающая свои аргументы в стандартный вывод, разделяя их пробелами и завершаясимволом конца строки. Если первым аргументом является -n, завершающий символновой строки опускается. (Это используется для приглашений к вводу из сценариев оболочки.)

 

#include<stdio.h>

int main (int argc, char **argv)

{ register int i, nflg;

nflg =0;

if(argc > 1 && argv[l][0] == '-' && argv[l][l] == n )

{ nflg++;

argc--; // уменьшение argc и увеличение argv явл. обычным пропуском

argv++; // аргументов командной строки

}

for(i=l; i<argc; i++)

{ fputs(argv[i], stdout) ;

if (i < argc-1)

putchar(' ') ;

}

if ( nflg == 0)

putchar('\n');

exit(0);

}

 

 

Разбор опций: getopt () и getopt_long ()

Примерно в 1980-х группа поддержки UNIX для System III в AT&T заметила, что каждая программа UNIX использовала для разбора аргументов свои собственные методики. Чтобы облегчить работу пользователей и программистов, они разработали большинство из перечисленных ранее соглашений. Группа поддержки UNIX разработала также функцию getopt(3), вместе с несколькими внешними переменными, чтобы упростить написание кода, придерживающегося стандартных соглашений. Функция GNU getopt_long(3) предоставляет совместимую с getopt(3) версию, а также упрощает разбор длинных опций в описанной ранее форме.

Функция getopt (3) представляет собой стандартный способ для последовательного разбора опций и их аргументов. GNU версия getopt (3) предоставляет некоторые расширенные возможности. GNU getopt_long(3) и getopt_long_only(3) позволяют легко разбирать длинные опции.

 

Знакомство с этими функциями предусмотрено в лаб. раб. №4. В пояснении к работе приведены их спецификация и описание.