Передача аргументов в программу
Бывает, что данные в программу передаются из командной строки при ее вызове. Такие данные называются аргументами командной строки. Выглядит это так, например:
./a.out test.txt ls -lt /home/peter/
Здесь вызываются программы a.out (из текущего каталога) и ls (из одного каталога, указанного в переменной окружения PATH). Первая программа из командной строки получает одно слово — test.txt, вторая — два: -lt и /home/peter/.
Если программа написана на языке C, то при ее запуске управление сразу передается в функцию main() , следовательно, именно она получает аргументы командной строки, которые присваиваются ее переменным-параметрам.
До этого мы определяли функцию main() так, как-будто она не принимает никакие параметры и ничего не возвращает. На самом деле в языке C любая функция по-умолчанию (если не определено ничего иного) возвращает целое число. В этом можно убедиться. Если записать код таким образом:
, то ошибки при компиляции не возникнет (но будет предупреждение). То же самое будет, если записать int main() . Это доказывает, что функция по-умолчанию возвращает целое число, а не ничто ( void ). Хотя то, что возвращает функция всегда можно «переопределить», например, voidmain() или float main() .
При вызове программы из командной строки в нее всегда передается пара данных:
- целое число, обозначающее количество слов (элементов, разделенных пробелами) в командной строке при вызове,
- указатель на массив строк, где каждая строка — это отдельное слово из командной строки.
Само имя программы также считается. Например, если вызов выглядит так:
, то первый аргумент программы имеет значение 4, а массив строк определяется как .
Обратите внимание на терминологию, есть всего два аргумента программы (число и массив), но сколько угодно аргументов командной строки. Аргументы командной строки «преобразуются» в аргументы программы (в аргументы функции main() ).
Эти данные (число и указатель) передаются в программу даже тогда, когда она просто вызывается по имени без передачи в нее чего-либо: ./a.out. В таком случае первый аргумент имеет значение 1, а второй указывает на массив, состоящий всего из одной строки .
То, что в программу передаются данные, вовсе не означает, что функция main() должна их принимать. Если функция main() определена без параметров, то получить доступ к аргументам командной строки невозможно. Хотя ничего вам не мешает их передавать. Ошибки не возникнет.
Чтобы получить доступ к переданным в программу данным, их необходимо присвоить переменным. Поскольку аргументы сразу передаются в main() , то ее заголовок должен выглядеть таким образом:
В первой переменной (n) содержится количество слов, а во второй — указатель на массив строк. Часто второй параметр записывают в виде **arr . Однако это то же самое. Вспомним, что сам массив строк, содержит в качестве своих элементов указатели на строки. А в функцию мы передаем указатель на первый элемент массива. Получается, что передаем указатель на указатель, т.е. **arr .
#include int main(int argc, char **argv) { int i; printf("%d\n", argc); for (i=0; i argc; i++) puts(argv[i]); }
Она выводит количество слов в командной строке при ее вызове и каждое слово с новой строки. Вызовите ее без аргументов командной строки и с аргументами.
В программе мы использовали переменные-параметры argc и argv. Принято использовать именно такие имена, но на самом деле они могут быть любыми. Лучше придерживаться этого стандарта, чтобы ваши программы были более понятны не только вам, но и другим программистам.
Практическое значение передачи данных в программу
Если у вас есть опыт работы в командной строке GNU/Linux, вы знаете, что у большинства команд есть ключи и аргументы. Например, при просмотре содержимого каталогов, копировании, перемещении в качестве аргументов указываются объекты файловой системы, над которыми выполняется команда. Особенности ее выполнения определяются с помощью ключей. Например, в команде
cp — это имя команды, -r — ключ, а ../les_1 и ../les_101 — аргументы команды.
Нередко в программы при их запуске передаются адреса файлов и «модификаторы» (это ключи) процесса выполнения программы.
Напишем программу, которая открывает указанные пользователем в командной строке файлы на запись или добавление и записывает (добавляет) туда одну и туже информацию, которую пользователь вводит с клавиатуры в процессе выполнения программы:
#include #include int main (int argc, char **argv) int i, ch; FILE *f[5]; if (argc 3 if (strcmp(argv[1], "-w") != 0 && strcmp(argv[1], "-a") != 0) { puts("Первый параметр -w или -a"); return 2; } for (i=0; i argc-2; i++){ f[i] = fopen(argv[i+2], argv[1]+1); if (f[i] == NULL) { printf("Файл %s нельзя открыть\n", argv[i+2]); return 3; } } while ((ch = getchar()) != EOF) for (i=0; i argc-2; i++) putc(ch,f[i]); for (i=0; i argc-2; i++) fclose(f[i]); return 0; }
- Создается массив из пяти файловых указателей. Следовательно можно одновременно открыть не более пяти файлов. Файловый указатель первого файла будет хранится в элементе массива f[0], второго — в f[1] и т.д.
- Проверяется количество аргументов командной строки. Их должно быть не меньше трех, т.к. первый — это имя программы, второй — режим открытия файла, третий — первый или единственный файл, в который будет производится запись. Поскольку программа позволяет открыть только пять файлов, то общее число аргументов командной строки не может быть больше семи. Поэтому если количество аргументов меньше 3 или больше 7, то программа завершается, т.к. оператор return приводит к выходу из функции, даже если после него есть еще код. Возвращаемое из функции значение неравное 0, может быть интерпретировано родительским процессом, как сообщение о том, что программа завершилась с ошибкой.
- Проверяется корректность второго аргумента командной строки. Если он не равен ни «-w», ни «-a», то условное выражение во втором if возвращает 1 (true). Функция strcmp() позволяет сравнивать строки и возвращает 0 в случае их равенства.
- В цикле for открываются файлы по указанным адресам, которые начинаются с третьего элемента массива argv. Именно поэтому к i прибавляется 2, чтобы получать элементы массива argv, начиная с третьего. Выражение argc-2 указывает на количество переданных имен файлов; т.к. в argc хранится общее число аргументов командной строки, первые два из которых не являются именами файлов.
- Выражение argv[1]+1 позволяет «вырезать» из строки «-w» (или «-a») подстроку «w» (или «a»), т.к. argv[1] по сути указатель на первый элемент строки. Прибавляя к указателю единицу, мы смещаем его к следующему элементу массива.
- Если файл отрыть не удается, то функция fopen() возвращает NULL. В таком случае программа завершается.
- Каждый символ, введенный пользователем с клавиатуры, записывается во все открытые файлы.
- В конце файлы закрываются.
Курс с решением части задач:
pdf-версия
Разбор опций командной строки в UNIX-подобных системах
Одной из важных задач любой программы, будь она консольной или графической является интерпретация аргументов командной строки. Формально аргументами называются все слова в командной строке(в том числе и имя самой команды) разбитые разделителем (как правило, это пробел и табуляция), кавычки же позволяют включать разделители в аргументы.
Аргументы можно подразделить на опции и операнды. Опции изменяют поведение программы или предоставляют ей дополнительную информацию. У опции могут быть свои аргументы, которые являются информацией необходимой только для этой опции.
POSIX
- в имени программы должно быть не менее 2 и не более 9 символов;
- имена программ должны быть написаны только строчными символами и цифрами;
- имя опции должно быть простым буквенно-цифровым символом. Опции с множеством цифр запрещены;
- все опции должны начинаться с символа «-«;
- для опций без аргументов должны быть реализована возможность объединения опций (например foo -a -b -c и foo -abc);
- аргумент опции должен отделятся от нее пробелом;
- аргумент опции не может быть необязательным;
- если опции требуется множество значений аргумента, они должны передаваться в виде строки, разделенные запятыми или разделителем;
- опции должны идти перед операндами;
- аргумент «—» указывает на окончание всех опций;
- порядок опций не должен играть роли, кроме случаев когда опции взаимоисключающие, тогда побеждает последняя;
- порядок аргументов может иметь значение;
- программы читающие или записывающие именованные файлы, должны трактовать единственный аргумент «-» как стандартный ввод или стандартный вывод соответственно.
Длинные опции
- каждая короткая опция должна иметь свой вариант длинной опции;
- длинную опцию можно сократить до кратчайшей строки, обеспечивающей ее уникальность;
- Аргументы длинной опции отделяются либо разделителем, либо знаком » ?» или «:» в зависимости от того найдена недействительная опция или пропущен обязательный аргумент опции, в переменной optopt будет находится обнаруженный недействительный символ.
Следует заметить, что стандартная функция getopt() останавливается сразу как только найдет первый аргумент начинающийся не с символа «-«, GNU вариант функции просматривает в поисках опций, всю командную строку. Поведение GNU функции можно изменить (но это выходит за рамки статьи).
пример программы с использованием getopt()
- #include
- #include
- #include
- int main( int argc, char **argv)
- if (argc == 1) < // если запускаем без аргументов, выводим справку
- printf( «getopt test\n» );
- printf( «usage:\n» );
- printf( » opts -a n -b m -o s\n» );
- printf( «example:\n» );
- printf( » $ opts -a 323 -b 23 -o ‘-‘\n» );
- printf( » 323 — 23 = 300\n» );
- return 0;
- >
- char *opts = «a:b:o:» ; // доступные опции, каждая принимает аргумент
- int a, b; // тут храним числа
- char op; // а тут оператор
- int opt; // каждая следующая опция попадает сюда
- while ((opt = getopt(argc, argv, opts)) != -1) < // вызываем getopt пока она не вернет -1
- switch (opt)
- case ‘a’ : // если опция -a, преобразуем строку с аргументом в число
- a = atoi(optarg);
- break ;
- case ‘b’ : // тоже для -b
- b = atoi(optarg);
- break ;
- case ‘o’ : // в op сохраняем оператор
- op = optarg[0];
- break ;
- >
- >
- switch (op)
- case ‘+’ : // если опаратор + складываем, и т.д.
- printf( «%d + %d = %d\n» , a, b, a + b);
- break ;
- case ‘-‘ :
- printf( «%d — %d = %d\n» , a, b, a — b);
- break ;
- case ‘*’ :
- printf( «%d * %d = %d\n» , a, b, a * b);
- break ;
- case ‘/’ :
- printf( «%d / %d = %d\n» , a, b, a / b);
- break ;
- >
- return 0;
- >
getopt_long()
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;
>
- 0 — не принимает аргумент;
- 1 — обязательный аргумент;
- 2 — необязательный аргумент.
Пример программы с использованием getopt_long()
- #include
- #include
- #include
- #include
- void usage( char *name)
- 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);
- return ;
- >
- int main ( int argc, char *argv[])
- int c;
- while (1)
- static struct option long_opt[] =
- < «help» , 0, 0, 'h' >,
- < «config» , 1, 0, 'c' >,
- >;
- int optIdx;
- if ((c = getopt_long(argc, argv, «c:h» , long_opt, &optIdx)) == -1)
- break ;
- switch ( c )
- case ‘h’ :
- usage(argv[0]);
- return (-1);
- case ‘c’ :
- printf( «option ‘c’ selected, filename: %s\n» , optarg);
- return (1);
- default :
- usage(argv[0]);
- return (-1);
- >
- >
- return (0);
- >
Заключение
В статье рассмотрены основы использования функций разбора аргументов командной строки в UNIX-подобных системах. Этот материал может быть более обширен, но любой заинтересовавшийся человек в состоянии узнать и изучить все тонкости самостоятельно.
Статья подготовлена по материалам книги Арнольда Роббинса «Linux программирование в примерах» ISBN 5-9579-0059-1