189 while (optind < argc && strchr(argv[optind], '=')) /* Установить
переменные окружения * /
190 putenv(argv[optind++]);
191
192 /* Если программа не указана, напечатать переменные окружения и выйти. */
193 if (optind == argc)
194 {
195 while (*environ)
196 puts (*environ++);
197 exit(EXIT_SUCCESS);
198 }
Строки 174–179
переносят существующие переменные в новую копию окружения. В глобальную переменную
environ
помещается указатель на пустой локальный массив. Параметр
envp
поддерживает доступ к первоначальному окружению.
Строки 181–184 удаляют переменные окружения, указанные в опции
– u
. Программа осуществляет это, повторно сканируя командную строку и удаляя перечисленные там имена. Удаление переменных окружения основывается на обсуждавшейся ранее особенности GNU
putenv
: при вызове с одним лишь именем переменной (без указанного значения)
putenv
удаляет ее из окружения.
После опций в командной строке помещаются новые или замещающие переменные окружения. Строки 189–190 продолжают сканирование командной строки, отыскивая установки переменных окружения в виде '
имя=значение
'.
По достижении строки 192, если в командной строке ничего не осталось, предполагается, что
env
печатает новое окружение и выходит из программы. Она это и делает (строки 195–197).
Если остались аргументы, они представляют имя команды, которую нужно вызвать, и аргументы для передачи этой новой команде. Это делается с помощью системного вызова
execvp
(строка 200), который замещает текущую программу новой. (Этот вызов обсуждается в разделе 9.1.4 «Запуск новой программы: семейство
exec
»; пока не беспокойтесь о деталях.) Если этот вызов возвращается в текущую программу, он потерпел неудачу. В таком случае
env
выводит сообщение об ошибке и завершает программу.
(определяемые в строке 203) соответствуют стандарту POSIX.
127
означает, что программа, которую
execvp
попыталась запустить, не существует. (
ENOENT
означает, что файл не содержит записи в каталоге.)
126
означает, что файл существует, но была какая-то другая ошибка.
2.5. Резюме
• Программы на С получают аргументы своей командной строки через параметры
argc
и
argv
. Функция
getopt
предоставляет стандартный способ для последовательного разбора опций и их аргументов GNU версия
getopt
предоставляет некоторые расширения, a
getopt_long
и
getopt_long_only
дает возможность легкого разбора длинных опций.
• Окружение представляет собой набор пар '
имя=значение
', который каждая программа наследует от своего родителя. Программы могут по прихоти своего автора использовать для изменения своего поведения переменные окружения, в дополнение к любым аргументам командной строки. Для получения значений переменных окружения, изменения их значений или удаления существуют стандартные процедуры (
getenv
,
setenv
,
putenv
и
unsetenv
). При необходимости можно получить доступ ко всему окружению через внешнюю переменную
environ
или через третий аргумент
char **envp
функции
main
. Последний способ не рекомендуется.
Упражнения
1. Предположим, что программа принимает опции
– a
,
– b
и
– с
, и что
– b
требует наличия аргумента. Напишите для этой программы код ручного разбора аргументов без использования
getopt
или
getopt_long
. Для завершения обработки опций принимается
– -
. Убедитесь, что -ас работает, также, как
– bYANKEES
,
– b YANKEES
и
– abYANKEES
. Протестируйте программу.
2. Реализуйте
getopt
. Для первой версии вы можете не беспокоиться насчет случая '
optstring[0] == ':'
'. Можете также игнорировать
opterr
.
3. Добавьте код для '
optstring[0] == ':'
' и
opterr
к своей версии
getopt
.
4. Распечатайте и прочтите файлы GNU
getopt.h
,
getopt.с
и
getopt1.с
.
5. Напишите программу, которая объявляет как
environ
, так и
envp
, и сравните их значения.
6. Разбор аргументов командной строки и опций является тем колесом, которое многие люди не могут не изобретать вновь. Вы можете захотеть познакомиться с различными анализирующими аргументы пакетами, помимо
getopt
и
getopt_long
, такими, как:
• библиотека анализа аргументов Plan 9 From Bell Labs arg(2)[31] ,
• Popt [37] . См. также справочную страницу popt(3) системы GNU/Linux.
7. Дополнительный балл, почему компилятор С не может полностью игнорировать ключевое слово register? Подсказка: какие действия невозможно совершать с регистровой переменной?