2.4.1. Функции управления окружением
Несколько функций позволяют получать значения переменных окружения, изменять эти значения или удалять их. Вот соответствующие объявления:
#include <stdlib.h>
char *getenv(const char *name);
/* ISO С: Получить переменную
окружения */
int setenv(const char *name, /* POSIX: Установить переменную */
const char *value, /* окружения */
int overwrite);
int putenv(char *string); /* XSI: Установить переменную
окружения, использует строку */
void unsetenv(const char *name); /* POSIX: Удалить переменную
окружения */
int clearenv(void); /* Общее: очистить все окружение */
Функция getenv()
— та, которую вы будете использовать в 99% случаев. Ее аргументом является имя переменной окружения, которую нужно искать, такое, как «НОМЕ
» или «PATH
». Если переменная существует, getenv()
возвращает указатель на строковое значение. Если нет, возвращается NULL
. Например:
char *pathval;
/* Поиск PATH; если нет, использовать значение
по умолчанию */
if ((pathval = getenv("PATH")) == NULL)
pathval = "/bin:/usr/bin:/usr/ucb";
Иногда переменная окружения существует, но с пустым значением. В этом случае возвращаемое значение не равно NULL
, но первый символ, на которую оно указывает, будет нулевым байтом, который в С является символом конца строки, '\0
'. Ваш код должен позаботиться проверить, что возвращаемое значение не равно NULL. Если оно не NULL
, необходимо также проверить, что строка не пустая, если вы хотите для чего-то использовать значение переменной. В любом случае, не используйте возвращенное значение слепо.
Для изменения переменной окружения или добавления к окружению еще одной используется setenv()
:
if (setenv("PATH", "/bin:/usr/bin:/usr/ucb", 1) != 0) {
/* обработать ошибку */
}
Возможно, что переменная уже существует в окружении. Если третий аргумент равен true (не ноль), новое значение затирает старое. В противном случае, предыдущее значение не меняется. Возвращаемое значение равно -1, если для новой переменной не хватило памяти, и 0 в противном случае. setenv()
для сохранения в окружении делает индивидуальные копии как имени переменной, так и нового ее значения
Более простой альтернативой setenv()
является putenv()
, которая берет одну строку «имя=значение
» и помещает ее в окружение:
if (putenv("PATH=/bin:/usr/bin:/usr/ucb") != 0) {
/* обработать ошибку */
}
putenv()
слепо заменяет любые предшествующие значения для той же переменной. А также, и это, возможно, более важно, строка, переданная putenv()
, помещается непосредственно в окружение. Это означает, что если ваш код позже изменит эту строку (например, если это был массив, а не строковая константа), окружение также будет изменено. Это, в свою очередь, означает, что вам не следует использовать в качестве параметров для putenv()
локальную переменную. По всем этим причинам setenv()
является более предпочтительной функцией.
ЗАМЕЧАНИЕ. GNU putenv()
имеет дополнительную (документированную) особенность в своем поведении. Если строка аргумента является именем без следующего за ним символа =
, именованная переменная удаляется. Программа GNU env
, которую мы рассмотрим далее в мой главе, полагается на такое поведение.
Функция unsetenv()
удаляет переменную из окружения:
unsetenv("PATH");
Наконец, функция clearenv()
полностью очищает окружение:
if (clearenv() != 0) {
/* обработать ошибку */
}
Эта функция не стандартизирована POSIX, хотя она доступна в GNU/Linux и нескольких коммерческих вариантах Unix. Ее следует использовать, если приложение должно быть очень безопасным и нужно построить собственное окружение с нуля. Если clearenv()
недоступна, в справке GNU/Linux для clearenv(3) рекомендуется использовать для выполнения этой задачи 'environ = NULL
'.
2.4.2. Окружение в целом: environ
Правильным способом работы с окружением является использование функций, описанных в предыдущем разделе. Однако, стоит взглянуть на то, как это работает «под капотом».
Внешняя переменная environ
предоставляет доступ таким же способом, как argv
предоставляет доступ к аргументам командной строки. Вы сами должны объявить переменную. Хотя она и стандартизирована POSIX, environ
намеренно не объявлена ни в одном стандартном заголовочном файле (Это, кажется, прослеживается из исторической практики.) Вот объявление:
extern char **environ; /* Смотрите, нет заголовочного файла POSIX */
Как и в argv
, завершающим элементом environ
является NULL
. Однако, здесь нет переменной «числа строк окружения», которая соответствовала бы argc
. Следующая простая программа распечатывает все окружение:
/* ch02-printenv.c --- Распечатать окружение. */
#include <stdio.h>
extern char **environ;
int main(int argc, char **argv) {
int i;
if (environ != NULL)
for (i = 0; environ[i] != NULL; i++)
printf("%s\n", environ[i]);
return 0;
}
Хотя это и маловероятно, перед попыткой использовать environ
эта программа проверяет, что она не равна NULL
.
Переменные хранятся в окружении в случайном порядке. Хотя некоторые оболочки Unix хранят переменные окружения в отсортированном по именам переменных виде, это формально не требуется, и многие оболочки не сортируют их.
В качестве уловки реализации можно получить доступ к окружению, объявив третий параметр main()
:
int main(int argc, char **argv, char **envp) {
...
}
Затем можно использовать envp
также, как environ
. Хотя это иногда можно увидеть в старом коде, мы не рекомендуем такое использование; environ
является официальным, стандартным, переносимым способом получения доступа ко всему окружению, если это вам необходимо.
2.4.3. GNU env
Чтобы завершить главу, рассмотрим GNU версию команды env
. Эта команда добавляет переменные к окружению в ходе выполнения одной команды. Она может использоваться также для очищения окружения в ходе этой команды или для удаления отдельных переменных окружения. Программа обеспечивает нас двойной функциональностью, поскольку проявляет возможности как getopt_long()
, так и несколько других возможностей, обсуждавшихся в этом разделе. Вот как вызывается программа:
$ env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]