<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://wiki2.linuxformat.ru/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>LXF112:Getopt - История изменений</title>
		<link>http://wiki2.linuxformat.ru/index.php?title=LXF112:Getopt&amp;action=history</link>
		<description>История изменений этой страницы в вики</description>
		<language>ru</language>
		<generator>MediaWiki 1.11.1</generator>
		<lastBuildDate>Wed, 13 May 2026 23:43:03 GMT</lastBuildDate>
		<item>
			<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
			<link>http://wiki2.linuxformat.ru/index.php?title=LXF112:Getopt&amp;diff=8898&amp;oldid=prev</link>
			<description>&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая статья&lt;/b&gt;&lt;/p&gt;&lt;div&gt;'''Командная строка''' Анализируем аргументы приложения в соответствии со стандартом [[Категория:Учебники]]&lt;br /&gt;
&lt;br /&gt;
==Правильные аргументы==&lt;br /&gt;
&lt;br /&gt;
: Театр начинается с вешалки, а приложение Unix – с аргументов командной строки. '''Артём Коротченко''' подскажет, как сделать так, чтобы первое впечатление пользователя было приятным.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Заголовок=О терминологии|Содержание=Рассмотрим команду ''cp -s file1 file2''. На самом деле, все четыре слова здесь являются аргументами, а то, что мы привыкли называть ими в повседневной жизни – это опции и операнды. Например, '''-s''' – это опция, а '''file1''' и '''file2''' – операнды. Ещё есть понятие «аргумент опции»:&lt;br /&gt;
&lt;br /&gt;
 /usr/bin/openssl des -in backup.tar.gz -out backup.sec&lt;br /&gt;
&lt;br /&gt;
В данном примере '''backup.tar.gz''' и '''backup.sec''' являются аргументами опций '''-in''' и '''-out'''.&lt;br /&gt;
Впрочем, мы не будем заострять внимание на этих тонкостях и используем привычную для большинства людей терминологию:&lt;br /&gt;
аргументы – это слова, следующие за именем команды.|Ширина=200px}}&lt;br /&gt;
&lt;br /&gt;
Подавляющее большинство ПО для Linux и Unix использует для своей работы аргументы командной строки. Даже программы с графическим интерфейсом, которым, казалось бы, и вовсе не нужна консоль, интерпретируют десятки входных параметров. Это может быть задание начальных настроек, включение отладочного режима или просто вывод версии программы без её запуска. В общем, аргументы используются повсеместно – так уж исторически сложилось в Unix-системах. Ну, а раз без аргументов никуда, надо уметь с ними работать. К счастью, библиотеки Unix и здесь придут вам на помощь... но давайте обо всем по порядку.&lt;br /&gt;
&lt;br /&gt;
===Вначале был ''argv''...===&lt;br /&gt;
&lt;br /&gt;
Основная функция любой ''C/C++'' программы описывается следующим образом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 int main (int argc, char *argv[] { . . . };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Или так – это вопрос вкуса:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 int main (int argc, char **argv) { . . . };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Массив '''argv''' содержит аргументы командной строки, '''argc''' указывает, сколько их всего было передано, причем имя программы&lt;br /&gt;
также включается сюда: оно доступно как '''argv[0]'''. Поэтому, чтобы&lt;br /&gt;
работать с командной строкой, самым простым программам вполне&lt;br /&gt;
хватало параметров функции '''main()'''.&lt;br /&gt;
&lt;br /&gt;
 # ./myinternetprogram&lt;br /&gt;
&lt;br /&gt;
Использование: ''./myinternetprogram &amp;lt;hostname&amp;gt;''&lt;br /&gt;
В этом случае весь процесс обработки аргументов сводится к проверке их количества.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 int main(int argc, char *argv[])&lt;br /&gt;
 {&lt;br /&gt;
             if (argc != 2)&lt;br /&gt;
             {&lt;br /&gt;
                          fprintf(stderr, &amp;quot;Использование: %s &amp;lt;hostname&amp;gt;\n&amp;quot;,&lt;br /&gt;
 argv[0]);&lt;br /&gt;
                          return 1;&lt;br /&gt;
             }&lt;br /&gt;
             ...&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Понятно, что когда весь синтаксис для запуска какой-то утилиты&lt;br /&gt;
исчерпывается одним или парой аргументов, не нужно изобретать&lt;br /&gt;
никаких новых библиотек.&lt;br /&gt;
&lt;br /&gt;
===Пришествие ''getopt()''===&lt;br /&gt;
&lt;br /&gt;
Трудности в мире Unix начались тогда, когда аргументов стало больше трёх. Конечно, дело тут даже не в количестве, а в том, что потребовалась большая гибкость при их анализе.&lt;br /&gt;
&lt;br /&gt;
  # cp --help&lt;br /&gt;
  Использование: cp [КЛЮЧ]... [-T] ИСТОЧНИК НАЗНАЧЕНИЕ&lt;br /&gt;
       или: cp [КЛЮЧ]... ИСТОЧНИК... КАТАЛОГ&lt;br /&gt;
       или: cp [КЛЮЧ]... -t КАТАЛОГ ИСТОЧНИК...&lt;br /&gt;
&lt;br /&gt;
Количество аргументов может меняться. Одни из них обязательные, другие – нет. Одним нужен дополнительный параметр&lt;br /&gt;
('''--backup[=CONTROL]'''), другим – не нужен ('''-l, -r, -s'''). Представляете,&lt;br /&gt;
как реализовать всю эту логику? Код усложнялся, рос всё больше и&lt;br /&gt;
больше. При этом каждая программа использовала свои методики&lt;br /&gt;
для разбора командной строки.&lt;br /&gt;
&lt;br /&gt;
Не говорю уже об эстетической стороне! У всех различный синтаксис. Кому-то нужен один дефис перед аргументами, кому-то&lt;br /&gt;
два, кому-то вообще не нужен. Кто-то обрамлял аргументы двойными кавычками, кто-то одинарными, а кто-то не выделял вообще.&lt;br /&gt;
Никакого единообразия. Так проблема постепенно перешла с плеч программистов на плечи пользователей (впрочем, в те далекие времена это было почти одно и то же).&lt;br /&gt;
&lt;br /&gt;
В общем, нужно было что-то делать. И тогда группой поддержки Unix был написан ряд POSIX-соглашений, который должен был&lt;br /&gt;
предоставить универсальное решение. Согласно им, все опции должны начинаться с дефиса и могут объединяться под этим символом (''./test -x -a -b'' – то же самое, что и ''./test -xab''). Опции не могут&lt;br /&gt;
состоять из цифр. Порядок аргументов не должен играть роли,&lt;br /&gt;
иначе это необходимо документировать. И так далее; желающие&lt;br /&gt;
ознакомиться с полным перечнем соглашений могут обратиться по&lt;br /&gt;
ссылке: http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html&lt;br /&gt;
&lt;br /&gt;
Итак, стандарт POSIX гарантировал пользователям Linux и Unix&lt;br /&gt;
удобную работу с программами, которые его придерживаются. Но&lt;br /&gt;
как быть с программистами? Ответ кроется в '''getopt()''', функции,&lt;br /&gt;
воплотившей требования POSIX в жизнь.&lt;br /&gt;
&lt;br /&gt;
===Немного практики===&lt;br /&gt;
&lt;br /&gt;
Начнем с приятного: пользоваться '''getopt()''' очень просто! Сложно&lt;br /&gt;
только ее понять. Собственно, синтаксис выглядит так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
  #include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
  int getopt(int argc, char * const argv[], const char *optstring);&lt;br /&gt;
  extern char *optarg;&lt;br /&gt;
  extern int optind, opterr, optopt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция вызывается в цикле, последовательно перебирая аргументы командной строки. На каждой итерации возвращается соответствующий аргумент. Удобно использовать конструкцию '''switch''' для того, чтобы смотреть, какая опция анализируется в настоящий&lt;br /&gt;
момент, и обрабатывать ее подходящим образом. Как только все&lt;br /&gt;
аргументы закончатся, функция вернёт '''-1'''. Возможны и другие возвращаемые значения – см. пример ниже.&lt;br /&gt;
&lt;br /&gt;
С выходом ''getopt()'' мы разобрались – а что на входе? '''argc''' и '''argv''',&lt;br /&gt;
понятно, получаются от '''main()'''. '''Optstring''' – это опции, которые ожидаем получить. Двоеточие после опции означает, что у неё должен быть аргумент:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
  int rs;&lt;br /&gt;
  opterr = 0;&lt;br /&gt;
  while ((rs = getopt(argc,argv,&amp;quot;:ab:c&amp;quot;)) != -1)&lt;br /&gt;
  {&lt;br /&gt;
     switch (rs)&lt;br /&gt;
     {&lt;br /&gt;
          case &amp;quot;a&amp;quot;: break;&lt;br /&gt;
          case &amp;quot;b&amp;quot;: fprintf(stderr, &amp;quot;%s - аргумент опции b&amp;quot;, optarg); break;&lt;br /&gt;
          case &amp;quot;c&amp;quot;: break;&lt;br /&gt;
          case &amp;quot;:&amp;quot;: fprintf(stderr, &amp;quot;%s: опции '-%c' требуется аргумент\n&amp;quot;, argv[0], optopt); exit(1);&lt;br /&gt;
          case &amp;quot;?&amp;quot;:&lt;br /&gt;
          default: fprintf(stderr, &amp;quot;%s: опция '-%c' неверна\n&amp;quot;, argv[0], optopt); exit(1);&lt;br /&gt;
     };&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Даже самые простые программы будут запускаться с неверными&lt;br /&gt;
опциями или недостающими аргументами. Это очевидно – людям&lt;br /&gt;
свойственно ошибаться. В этом случае функция '''getopt()''' выводит&lt;br /&gt;
сообщение об ошибке, но мы можем препятствовать такому поведению (например, чтобы уведомить пользователя о некорректной&lt;br /&gt;
опции каким-то другим образом). Этого можно достичь двумя способами: либо указать первым символом '''optstring''' двоеточие, либо&lt;br /&gt;
приравнять переменную '''opterr''' нулю.&lt;br /&gt;
&lt;br /&gt;
Приведённый выше пример использует оба сразу. Как видно,&lt;br /&gt;
'''getopt()''' будет возвращать :, если для опции не указан аргумент, и '''?''',&lt;br /&gt;
если введена вообще неверная опция. Учтите: в случае, когда первым символом '''optstring''' не является двоеточие, '''getopt()''' возвращает '''?''' для обоих видов ошибок.&lt;br /&gt;
&lt;br /&gt;
Теперь настало время сказать о переменных:&lt;br /&gt;
* '''optarg''' – аргумент опции. В нашем случае он присутствует только для '''-b'''.&lt;br /&gt;
* '''optind''' – индекс просматриваемого в данный момент элемента массива '''argv'''.&lt;br /&gt;
* '''opterr''' – как уже было сказано, нужно приравнять нулю, если мы хотим запретить '''getopt''' выводить свои сообщения об ошибках.&lt;br /&gt;
* '''optopt''' – имя опции (если '''getopt''' вернула ''':''' или '''?''', а нам её всё равно нужно узнать).&lt;br /&gt;
&lt;br /&gt;
===Длинная история===&lt;br /&gt;
&lt;br /&gt;
Программы GNU используют длинные опции, которые начинаются с&lt;br /&gt;
двух дефисов и содержат полные английские слова. Их можно сочетать с короткими опциями (например, '''-R''' и '''--recursive''', '''-h''' и '''--help'''), а&lt;br /&gt;
можно и не сочетать, т.е. использовать в одиночку. Чтобы анализировать длинные опции, существует функция '''getopt_long()''' (вообще, я бы советовал только ею и ограничиться). Наряду с '''getopt_long()'''&lt;br /&gt;
существует функция '''getopt_long_only()''': она не работает с короткими опциями, хотя в остальном аналогична '''getopt_long()'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 #include &amp;lt;getopt.h&amp;gt;&lt;br /&gt;
 int getopt_long(int argc, char * const argv[],&lt;br /&gt;
        const char *optstring,&lt;br /&gt;
        const struct option *longopts, int *longindex);&lt;br /&gt;
 int getopt_long_only(int argc, char * const argv[],&lt;br /&gt;
        const char *optstring,&lt;br /&gt;
        const struct option *longopts, int *longindex);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''longindex''' связан с диагностикой ошибок и в общем случае не нужен – передаём '''NULL'''. Первые три параметра аналогичны тем, что мы указывали для '''getopt()'''. Вопросы может вызвать только структура '''longopts'''.&lt;br /&gt;
Синтаксис у неё следующий:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
 struct option {&lt;br /&gt;
        const char *name;&lt;br /&gt;
        int has_arg;&lt;br /&gt;
        int *flag;&lt;br /&gt;
        int val;&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''name''' – имя длинной опции.&lt;br /&gt;
* '''has_arg''' – нужен ли ей аргумент. В качестве значения можно указать '''no_argument''' (не нужен), '''required_argument''' (нужен) или '''optional_argument''' (аргумент может быть, а может и не быть). Все три значения являются макроподстановками из '''getopt.h''' и равны '''0, 1''' и '''2''', соответственно.&lt;br /&gt;
* '''flag''' – либо '''NULL''', либо указатель на переменную. В первом случае '''getopt_long()''' возвращает значение поля '''val''' этой структуры. Во втором '''getopt_long()''' возвращает '''0''', а переменная, на которую указывает '''flag''', заполняется значением '''val'''.&lt;br /&gt;
* '''val''' – имя короткой опции, которая соответствует длинной опции, если '''flag''' равен '''NULL'''. Для случая, когда '''flag''' указывает на переменную, это значение, которое в него запишется, если программа встретит такой аргумент.&lt;br /&gt;
&lt;br /&gt;
Согласен, все это звучит несколько путано, поэтому проиллюстрируем вышесказанное примером:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
  struct option longopts[] =&lt;br /&gt;
  {&lt;br /&gt;
              { &amp;quot;help&amp;quot;, no_argument, &amp;amp;do_help, 1 },&lt;br /&gt;
              { &amp;quot;version&amp;quot;, no_argument, &amp;amp;do_version, 1 },&lt;br /&gt;
              { &amp;quot;verbose&amp;quot;, no_argument, NULL, 'v' }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если пользователь ввёл опцию '''--help''', '''do_help''' будет равен '''1'''. Если&lt;br /&gt;
не ввёл – значение '''do_help''' при вызове '''getopt_long()''' не изменится.&lt;br /&gt;
Аналогично происходит и с '''--version'''.&lt;br /&gt;
&lt;br /&gt;
Вообще, возможность использовать флаговые переменные таким образом хороша тем, что не нужно самостоятельно указывать в цикле '''getopt_long()''', что делать после выхода из него. Зато&lt;br /&gt;
нельзя использовать короткие опции. Для '''--verbose''' придётся установить флаговую переменную в цикле вручную, зато будет работать опция '''-v'''.&lt;br /&gt;
&lt;br /&gt;
===Все вместе===&lt;br /&gt;
&lt;br /&gt;
Чтобы свести полученные знания воедино, давайте напишем Unix-команду '''''yes''''', совершенно безумную утилиту, цель которой – выводить принятый аргумент до тех пор, пока её не убьют (полный текст можно найти на диске).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
  int main(int argc, char *argv[])&lt;br /&gt;
  {&lt;br /&gt;
       ...&lt;br /&gt;
       struct option longopts[] =&lt;br /&gt;
       {&lt;br /&gt;
            { &amp;quot;help&amp;quot;, no_argument, &amp;amp;do_help, 1 },&lt;br /&gt;
            { &amp;quot;version&amp;quot;, no_argument, &amp;amp;do_version, 1 },&lt;br /&gt;
       };&lt;br /&gt;
       myname = argv[0];&lt;br /&gt;
       opterr = 0;&lt;br /&gt;
       while ((rs = getopt_long(argc, argv, &amp;quot;-&amp;quot;, longopts, NULL)) != -1)&lt;br /&gt;
       {&lt;br /&gt;
            switch (rs)&lt;br /&gt;
            {&lt;br /&gt;
                   case 0:&lt;br /&gt;
                         break;&lt;br /&gt;
                   case 1:&lt;br /&gt;
                         txt = xrealloc(txt, strlen(optarg) + strlen(txt) + 2);&lt;br /&gt;
                         strcat(txt, optarg);&lt;br /&gt;
                         strcat(txt, &amp;quot; &amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                   case ':':&lt;br /&gt;
                         fprintf(stderr, &amp;quot;%s: опции '-%c' требуется аргумент\n&amp;quot;, argv[0], optopt);&lt;br /&gt;
                         return 1;&lt;br /&gt;
                   case '?':&lt;br /&gt;
                   default:&lt;br /&gt;
                       fprintf(stderr, &amp;quot;%s: опция '-%c' неверна\n&amp;quot;, argv[0], optopt);&lt;br /&gt;
                       return 1;&lt;br /&gt;
            }&lt;br /&gt;
      }&lt;br /&gt;
      if (do_help)&lt;br /&gt;
      {&lt;br /&gt;
            ...&lt;br /&gt;
      }&lt;br /&gt;
      if (do_version)&lt;br /&gt;
      {&lt;br /&gt;
            ...&lt;br /&gt;
      }&lt;br /&gt;
      if (argc &amp;lt; 2)&lt;br /&gt;
            strcpy(txt, &amp;quot;y&amp;quot;);&lt;br /&gt;
      while (1)&lt;br /&gt;
            printf(&amp;quot;%s\n&amp;quot;, txt);&lt;br /&gt;
      return 0;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скомпилируйте программу командой&lt;br /&gt;
 gcc fakeyes.c -o fakeyes&lt;br /&gt;
и начинайте тестирование. Кстати, наш ''yes'' ничем не уступает оригиналу.&lt;br /&gt;
&lt;br /&gt;
 ./fakeyes --help&lt;br /&gt;
 ./fakeyes --version&lt;br /&gt;
 ./fakeyes -c&lt;br /&gt;
 ./fakeyes&lt;br /&gt;
 ./fakeyes хорошо отлично&lt;br /&gt;
&lt;br /&gt;
'''LXF'''&lt;br /&gt;
&lt;br /&gt;
===Не только для ''C''===&lt;br /&gt;
&lt;br /&gt;
Функции ''getopt()'' и ''getopt_long()'' были разработаны для ''С'', но&lt;br /&gt;
понятно, что другие языки, такие как ''Java, Perl, Python, PHP'',&lt;br /&gt;
не могли обойти своим вниманием столь мощные возможности&lt;br /&gt;
анализа аргументов. Существует реализация и для оболочки:&lt;br /&gt;
в пакет ''util-linux'', который присутствует в большинстве дистрибутивов, входит утилита ''getopt''. С её помощью можно разбирать&lt;br /&gt;
аргументы сценариев. Например:&lt;br /&gt;
&lt;br /&gt;
 #!/bin/bash&lt;br /&gt;
 rs=`getopt ab:c $*`&lt;br /&gt;
 set -- $rs&lt;br /&gt;
 for i&lt;br /&gt;
 do&lt;br /&gt;
             echo &amp;quot;$i&amp;quot;&lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Проверим&lt;br /&gt;
&lt;br /&gt;
 ./test -x -a -b&lt;br /&gt;
 getopt: invalid option -- x&lt;br /&gt;
 getopt: option requires an argument -- b&lt;br /&gt;
 -a&lt;br /&gt;
 --&lt;br /&gt;
&lt;br /&gt;
В качестве альтернативы можно использовать ''getopts''.&lt;/div&gt;</description>
			<pubDate>Fri, 09 Oct 2009 05:55:57 GMT</pubDate>			<dc:creator>Crazy Rebel</dc:creator>			<comments>http://wiki2.linuxformat.ru/index.php/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:LXF112:Getopt</comments>		</item>
	</channel>
</rss>