Книга: Linux программирование в примерах
2.4.3. GNU env
2.4.3. GNU env
Чтобы завершить главу, рассмотрим GNU версию команды env
. Эта команда добавляет переменные к окружению в ходе выполнения одной команды. Она может использоваться также для очищения окружения в ходе этой команды или для удаления отдельных переменных окружения. Программа обеспечивает нас двойной функциональностью, поскольку проявляет возможности как getopt_long()
, так и несколько других возможностей, обсуждавшихся в этом разделе. Вот как вызывается программа:
$ env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
/* Устанавливает соответствующее VALUE для каждого NAME и запускает COMMAND */
-i, --ignore-environment /* запустить с пустым окружением */
-u, --unset=NAME /* удалить переменную из окружения */
--help /* показать этот экран справки и выйти */
--version /* вывести информацию о версии и выйти */
/* Простое - предполагает -1. Если не указана COMMAND, отображает
имеющееся окружение.
Об ошибках сообщайте в <[email protected]>. */
Вот несколько примеров вызовов команды:
$ env - myprog arg1 /* Очистить окружение, запустить программу с args */
$ env - РАТН=/bin:/usr/bin myprog arg1 /* Очистить окружение, добавить PATH, запустить программу */
$ env -u IFS PATH=/bin:/usr/bin myprog arg1 /* Сбросить IFS, добавить PATH, запустить программу */
Код начинается со стандартной формулировки авторских прав GNU и разъясняющего комментария. Мы для краткости их опустили. (Формулировка авторского права обсуждается в Приложении С «Общедоступная лицензия GNU». Показанного ранее вывода --help
достаточно для понимания того, как работает программа.) За объявленным авторским правом и комментарием следуют подключаемые заголовочные файлы и объявления. Вызов макроса 'N_("string")
' (строка 93) предназначен для использования при локализации программного обеспечения, тема, освещенная в главе 13 «Интернационализация и локализация». Пока вы можете рассматривать его, как содержащий строковую константу.
80 #include <config.h>
81 #include <stdio.h>
82 #include <getopt.h>
83 #include <sys/types.h>
84 #include <getopt.h>
85
86 #include "system.h"
87 #include "error.h"
88 #include "closeout.h"
89
90 /* Официальное имя этой программы (напр., нет префикса 'g'). */
91 #define PROGRAM_NAME "env"
92
93 #define AUTHORS N_ ("Richard Mlynarik and David MacKenzie")
94
95 int putenv();
96
97 extern char **environ;
98
99 /* Имя, посредством которого эта программа была запущена. */
100 char *program_name;
101
102 static struct option const longopts[] =
103 {
104 {"ignore-environment", no_argument, NULL, 'i'},
105 {"unset", required_argument, NULL, 'u'},
106 {GETOPT_HELP_OPTION_DECL},
107 {GETOPT_VERSION_OPTION_DECL},
108 {NULL, 0, NULL, 0}
109 };
GNU Coreutils содержит большое число программ, многие из которых выполняют одни и те же общие задачи (например, анализ аргументов). Для облегчения сопровождения многие типичные идиомы были определены в виде макросов. Двумя таким макросами являются GETOPT_HELP_OPTION_DECL
и GETOPT_VERSION_OPTION
(строки 106 и 107). Вскоре мы рассмотрим их определения. Первая функция, usage()
, выводит информацию об использовании и завершает программу. Макрос _("string")
(строка 115, используется также по всей программе) также предназначен для локализации, пока также считайте его содержащим строковую константу.
111 void
112 usage(int status)
113 {
114 if (status '= 0)
115 fprintf(stderr, _("Try '%s --help' for more information.n"),
116 program_name);
117 else
118 {
119 printf (_("
120 Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]n"),
121 program_name);
122 fputs (_("
123 Set each NAME to VALUE in the environment and run COMMAND. n
124 n
125 -i, --ignore-environment start with an empty environmentn
126 -u, --unset=NAME remove variable from the environmentn
127 "), stdout);
128 fputs(HELP_OPTION_DESCRIPTION, stdout);
129 fputs(VERSION_OPTION_DESCRIPTION, stdout);
130 fputs(_("
131 n
132 A mere - implies -i. If no COMMAND, print the resulting
133 environment.n"), stdout);
134 printf(_("nReport bugs to <%s>.n"), PACKAGE_BUGREPORT);
135 }
136 exit(status);
137 }
Первая часть main()
объявляет переменные и настраивает локализацию. Функции setlocale()
, bindtextdomain()
и textdomain()
(строки 147–149) обсуждаются в главе 13 «Интернационализация и локализация». Отметим, что эта программа использует аргумент main() envp
(строка 140). Это единственная программа Coreutils, которая так делает. Наконец, вызов atexit()
в строке 151 (см. раздел 9.1.5.3. «Функции завершения») регистрирует библиотечную функцию Coreutils, которая очищает все выходные буферы и закрывает stdout
, выдавая сообщение при ошибке. Следующая часть программы обрабатывает аргументы командной строки, используя getopt_long()
.
139 int
140 main(register int argc, register char **argv, char **envp)
141 {
142 char *dummy_environ[1];
143 int optc;
144 int ignore_environment = 0;
145
146 program_name = argv[0];
147 setlocale(LC_ALL, "");
148 bindtextdomain(PACKAGE, LOCALEDIR);
149 textdomain(PACKAGE);
150
151 atexit(close_stdout);
152
153 while ((optc = getopt_long(argc, argv, "+iu:", longopts, NULL)) != -1)
154 {
155 switch (optc)
156 {
157 case 0:
158 break;
159 case 'i':
160 ignore_environment = 1;
161 break;
162 case 'u':
163 break;
164 case_GETOPT_HELP_CHAR;
165 case_GETOPT_VERSION_CHAR(PROGRAM_NAME, AUTHORS);
166 default:
167 usage(2);
168 }
169 }
170
171 if (optind != argc && !strcmp(argv[optind], "-"))
172 ignore_environment = 1;
Вот отрывок из файла src/sys2.h
в дистрибутиве Coreutils с упомянутыми ранее определениями и макросом 'case_GETOPT_xxx
', использованным выше (строки 164–165):
/* Вынесение за скобки общей части кода, обрабатывающего --help и
--version. */
/* Эти значения перечисления никак не могут конфликтовать со значениями опций,
обычно используемыми командами, включая CHAR_MAX + 1 и т.д. Избегайте
CHAR_MIN - 1, т.к. оно может равняться -1, значение завершения опций getopt.
*/
enum {
GETOPT_HELP_CHAR = (CHAR_MIN — 2),
GETOPT_VERSION_CHAR = (CHAR_MIN - 3)
};
#define GETOPT_HELP_OPTION_DECL
"help", no_argument, 0, GETOPT_HELP_CHAR
#define GETOPT_VERSION_OPTION_DECL
"version", no_argument, 0, GETOPT_VERSION_CHAR
#define case_GETOPT_HELP_CHAR
case GETOPT_HELP_CHAR:
usage(EXIT_SUCCESS);
break;
#define case_GETOPT_VERSION_CHAR(Program_name, Authors)
case GETOPT_VERSION_CHAR:
version_etc(stdout, Program_name, PACKAGE, VERSION, Authors);
exit(EXIT_SUCCESS);
break;
Результатом этого кода является печать сообщения об использовании утилиты для --help
и печать информации о версии для --version
. Обе опции завершаются успешно («Успешный» и «неудачный» статусы завершения описаны в разделе 9.1.5.1 «Определение статуса завершения процесса».) Поскольку в Coreutils входят десятки утилит, имеет смысл вынести за скобки и стандартизовать как можно больше повторяющегося кода.
Возвращаясь к env.с
:
174 environ = dummy_environ;
175 environ[0] = NULL;
176
177 if (!ignore_environment)
178 for (; *envp; envp++)
179 putenv(*envp);
180
181 optind = 0; /* Принудительная реинициализация GNU getopt. */
182 while ((optc = getopt_long(argc, argv, "+iu:", longopts, NULL)) != -1)
183 if (optc == 'u')
184 putenv(optarg); /* Требуется GNU putenv. */
185
186 if (optind !=argc && !strcmp(argv[optind], "-")) /* Пропустить опции */
187 ++optind;
188
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
выводит сообщение об ошибке и завершает программу.
200 execvp(argv[optind], &argv[optind]);
201
202 {
203 int exit_status = (errno == ENOENT ? 127 : 126);
204 error(0, errno, "%s", argv[optind]);
205 exit(exit_status);
206 }
207 }
Значения кода завершения 126
и 127
(определяемые в строке 203) соответствуют стандарту POSIX. 127
означает, что программа, которую execvp()
попыталась запустить, не существует. (ENOENT
означает, что файл не содержит записи в каталоге.) 126
означает, что файл существует, но была какая-то другая ошибка.
- Листинг 7.4. (print-environment.c) Отображение переменных среды процесса
- Листинг 2.3. (print-env.c) Вывод переменных среды
- Appendix H. GNU Free Documentation License
- Appendix I. GNU General Public License
- 1.3. Автоматизация процесса с помощью GNU-утилиты make
- The GNOME Desktop Environment
- KDE — The Other Environment
- Using Environment Variables
- The GNU Image Manipulation Program
- 8.5. Обход дерева файлов: GNU du
- GNU Emacs and the Free Software Foundation
- Проект GNU и Фонд свободного ПО