W зарезервирована POSIX.2 для реализации расширений. Такое поведение является дополнительным для GNU и недоступным в библиотеках GNU libc более ранних по сравнению со второй версий.
По умолчанию getopt переставляет элементы содержимого argv в процессе поиска так, что, в конечном счёте, все аргументы, не являющиеся опциями, оказываются в конце. Возможны два других режима работы функции.
4.Если первым символом optstring является «+» или задана переменная окружения POSIXLY_CORRECT, то обработка опций прерывается на первом аргументе, не являющемся опцией.
5.Если первым символом optstring является «-»(тире), то каждый элемент argv, не являющийся опцией, обрабатывается так, как если бы он был аргументом опции с символом, имеющим код 1 (один). Такой режим работы используется программами, которые требуют опции и другие аргументы командной строки.
6.Специальный аргумент «--» (два тире) служит для обозначения конца опций независимо от режима работы функции getopt.
Если getopt не распознал символ опции, то он выводит в стандартный поток ошибок соответствующее сообщение, заносит символ в переменную optopt и возвращает «?». Вызывающая программа может предотвратить вывод сообщения об ошибке, установив нулевое значение opterr.
Если найден параметр, начинающийся с двух символов «-» (тире), то считается, что найдена длинная опция. В этом случае производится сравнение найденного аргумента (исключая двойное тире) со значениями name массива longopts.
Если совпадение найдено, то значение val помещается либо по адресу, указанному в flag, и getopt_long завершается с результатом 0, либо getopt_long завершается с результатом val. В любом из двух случаев, если параметр longindex не равен NULL, то по указанному в нём адресу помещается номер элемента массива, с которым совпала найденная длинная опция.
Длинная опция может иметь параметр, указываемый через знак равенства (--опция=параметр) или через пробел (--опция параметр). Наличие параметра указывается в поле has_arg.
Функция getopt_long_only работает так же, как getopt_long, но обрабатывает только длинные опции. В качестве указателя опции могут служить не только символы «--», но и «-». Если опция, начинающаяся с «-» (не
с«--»), не совпадает с длинной опцией, но совпадает с короткой, то она обрабатывается как короткая опция.
Пример программы, анализирующей опции командной строки, представлен в листинге 10.
Листинг 10. Работа с переменными среды окружения
1.#include <stdio.h>
2.#include <stdlib.h>
3.#include <getopt.h>
4.
81
Листинг 10. Работа с переменными среды окружения
5.int main (int argc, char **argv){
6.int c;
7.int digit_optind = 0;
8.int this_option_optind;
9.int option_index = 0;
10.static struct option long_options[] = {
11.{“add”, 1, 0, 0},
12.{“append”, 0, 0, 0},
13.{“delete”, 1, 0, 0},
14.{“verbose”, 0, 0, 0},
15.{“create”, 1, 0, ‘c’},
16.{“file”, 1, 0, 0},
17.{0, 0, 0, 0}};
18.while (1){
19.this_option_optind = optind ? optind : 1;
20.c = getopt_long (argc, argv, “abc:d:012”,
21.
22.if (c
23.switch
24.case
25.
26.
27.
28.
29.
long_options, &option_index); == -1) break;
(c){
0: printf (“параметр %s”, long_options[option_index].name);
if (optarg) printf (“ с аргументом %s”, optarg);
printf (“\n”); break;
30.case ‘0’:
31.case ‘1’:
32.case ‘2’: if (digit_optind != 0 &&
33.
34.
35.
36.
37.
digit_optind != this_option_optind) printf (“цифры используются дважды.\n”);
digit_optind = this_option_optind; printf (“параметр %c\n”, c); break;
38.case ‘a’: printf (“параметр a\n”);
39. break;
40.case ‘b’: printf (“параметр b\n”);
41. break;
42.case ‘c’: printf (“c со значением ‘%s’\n”,
43.
44.
optarg);
break;
45.case ‘d’: printf (“d со значением ‘%s’\n”,
46.
47.
48.
optarg);
break; case ‘?’: break;
82
Листинг 10. Работа с переменными среды окружения
49.default: printf (“результат 0%o ??\n”, c);
50.}
51.}
52.if (optind < argc){
53.printf (“элементы ARGV, не параметры: ”);
54.while (optind < argc)
55.printf (“%s ”, argv[optind++]);
56.printf (“\n”);
57.}
58.exit (0);
59.}
6.1.2. Взаимодействие Си-программы со средой окружения
Массив envp[], передаваемый третьим аргументом в функцию main, содержит указатели на переменные среды окружения. Каждый элемент массива является указателем на строку вида: «имя_переменной = значение». Последним элементом массива является указатель NULL.
Стандарт ANSI С определяет только два первых параметра функции main – argc и argv. Стандарт POSIX.1 определяет также параметр envp, хотя рекомендует передачу окружения программы производить через глобальную переменную environ:
extern char ** environ;
Формат массива environ такой же, как и у env. Пример вывода переменных среды окружения с использованием двух массивов приведён в листинге 11.
Листинг 11. Вывод информации о командной строке
1.#include <stddef.h>
2.#include <stdio.h>
3.extern char **environ;
4.int main(int argc, char *argv[]){
5.int i;
6.printf (“Запущена программа %s. ”, argv[0]);
7.printf (“Число параметров %d\n”, argc-1);
8.for (i = 1; i < argc; i ++)
9.printf(“argv[%d] = %s\n”, i, argv[i]);
10.for (i = 0; i < 8; i ++)
11.if (environ[i] != NULL)
12.printf(“environ[%d] : %s\n”,i, environ[i]);
13.}
Для доступа к переменным окружения по их именам используются функции: getenv и putenv:
83
#include <stdlib.h>
char *getenv(const char *name); int putenv (const char *string);
Функция getenv возвращает значение переменной окружения с именем name, a putenv помещает переменную и её значение в окружение процесса. Параметр string функции putenv содержит строку вида var_name = var_value.
В качестве примера приведём программу, похожую по своей функциональности на предыдущую, выводящую выборочно значения переменных и устанавливающую новые значения по желанию пользователя.
Листинг 12. Работа с переменными среды окружения
1.#include <stddef.h>
2.#include <stdlib.h>
3.#include <stdio.h>
4.
5.int main(int argc, char *argv[]){
6.char *term;
7.char buf[200], var[200];
8.
9./* Проверим, определена ли переменная TERM */
10.if ((term = getenv(“TERM”)) == NULL){
11./* Если переменная не определена, получим от *
12. |
* |
пользователя |
её значение и поместим |
* |
13. |
* |
переменную в |
окружение программы |
*/ |
14.printf (“Переменная TERM не определена.\n”);
15.printf (“Введите её значение: ”);
16.gets(buf);
17.sprintf(var, “TERM=%s”,buf);
18.putenv(var);
19.printf(“%s\n”, var);
20.}
21.else {
22./* Если переменная TERM определена, предоставим *
23.* пользователю возможность изменить её значение,*
24. |
* после чего поместим её в окружение процесса |
*/ |
25.printf(“TERM=%s. Change? [N]”, term);
26.gets(buf);
27.if (buf[0] == ‘Y’ || buf[0] == ‘y’){
28.printf(“TERM=”); gets(buf);
29.sprintf(var, “TERM=%s”, buf); putenv(var);
30.printf(“new %s\n”, var);
31.}
32.}
33.}
84
Встроке 11 проверяется, определена ли переменная TERM. Если переменная TERM не определена, функция getenv возвращает NULL.
Встроках 17 и 29 формируются аргументы для функции putenv, содержащие новые значения переменной TERM. Далее, в строках 18 и 29 соответсвенно, устанавливается новое значение для переменной TERM.
Введённое значение переменной будет действительно только для данного процесса и порождённых им процессов: если после завершения программы вывести значение TERM, то видно, что оно не изменилось.
6.2.Взаимодействие «родственных» процессов и нитей
6.2.1. Ожидание завершения дочерних процессов
При выполнении нескольких процессов в операционной системе, и особенно «родственных» процессов, часто возникает необходимость выполнить какое-либо действие всеми процессами одновременно. Примером может служить событие завершения программы. Например, родительский процесс должен перед завершением удалить все временные файлы, создаваемые в ходе работы программы. Очевидно, что если он завершится раньше, чем дочерний процесс закончит работать с временными файлами, то логика работы программы может быть существенно нарушена. Поэтому требуется, чтобы родительский процесс «дождался», пока завершится дочерний, и только потом удалил все файлы.
Одним из способов такой синхронизации является обработка родителем сигнала SIGCHLD, отправляемого ему при завершении дочернего процесса. Такой подход требует определённых настроек родительского процесса, в частности, определения, чем он будет заниматься, пока будет «ожидать» сигнал (активное и пассивное ожидание). Ещё одним недостатком использования сигнала SIGCHILD является тот факт, что, во-первых, он ничего не сообщает о статусе завершения процесса, а, во-вторых, при наличии нескольких потомков нельзя будет определить, какой именно из них завершил свою работу.
Последнего недостатка лишены функции семейства wait: wait, waitid и waitpid:
#include <sys/types.h> #include <sys/wait.h>
pid_t wait(int *stat_loc);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
Первый вызов wait позволяет заблокировать выполнение процесса, пока кто-либо из его непосредственных потомков не прекратит существование. По сути, это аналог использования сигнала SIGCHILD и «сна» в процессе ожидания его появления. Отличие только в том, что статус завершённого
85