7. Переменные окружения#

Введение#

Существует несколько способов передачи данных в программу, которые быстро реализуемы функциями стандартной библиотеки Си/C++ – это:

  • чтение из файла;

  • прием данных из клавиатуры;

  • сканирование аргументов командной строки.

Но также существует четвертый, менее известный, но такой же простой способ как и остальные выше: через переменные окружения. В отличие от файлов и клавиатуры, переменные окружения обладают “малой информационной емкостью”: через них можно передать только несколько десятков символов. Поэтому они используются в качестве передачи конфигурационных данных от системных программ к пользовательским. Отсюда и пошло их название – переменные окружения, что значит они несут приложениям информацию об окружении, а приложения могут воспользоваться ими, чтобы подстроиться под него.

Переменная окружения представляет две строки из печатных символов, обозначающих имя и значение, разделенных символом =. Имя переменной принято составлять из заглавных букв. Значение переменной может быть интерпретировано как имя или путь к файлу или каталогу, список каталогов, число или произвольная строка. Значение может быть интерпретировано как список, элементы которых разделены символом :.

В таблице ниже перечислены переменные системы UNIX, часто встречаемые на практике.

Имя переменной

Предназначение

HOME

Домашний каталог пользователя

PWD

Текущий каталог

PATH

Список каталогов, откуда осуществляется поиск запускаемых по имени программ

USER

Имя пользователя

HOSTNAME

Имя компьютера

SHELL

Полный путь к исполняемому файлу системного интерпретатора

TZ

Часовой пояс

LANG

Текущие язык, настройки локализации и кодировка

EDITOR

Используемый по умолчанию текстовый редактор

Зачем нужны переменные? Переменные несут информацию от системы к пользователю. Они подсказывают приложениям, в каком каталоге находился пользователь перед запуском приложения (PWD), в каком часовом поясе он находится (TZ), какой язык и кодировку использует система (LANG) и какой редактор предпочитает пользователь (EDITOR). Переменные также переносят информацию в обратном направлении: от пользователя к системе. Пользователь с помощью них подсказывает системе где искать программы для запуска (PATH) и разделяемые библиотеки (LD_LIBRARY_PATH), каким количеством потоков запускать программу (OMP_NUM_THREADS).

Идея с переменными окружения реализована во всех современных ОС. Несмотря на простую идею, существуют два небольших отличий, о которых следует знать при разработке кроссплатформенных программ.

  • Имена переменных в UNIX чувствительны к регистру используемых символов, а в Windows – нет. Это значит, что в UNIX переменные PATH и Path – это две отдельные переменные, а в Windows – одна переменная. Такое различие в требованиях также характерно языкам программирования Си и Fortran применительно к именам программных объектов – переменным, функциям и процедурам, структурам и классам. В отличие от большинства языков программирования, обращение к отсутствующей переменной окружения вернет пустую строку, а не предупредит разработчика об ошибке в инструкции. Чтобы не попадаться на такие сложнообнаруживаемые ошибки, придержваются правилу использовать заглавные буквы в имени.

  • В переменных, которые содержат список, для разделения элементов в UNIX используют символ :, а в Windows – ;. Из этого следует, что лучше воздержаться от использования этих специальных символов в других смыслах, чтобы не вести в заблуждение остальных разработчиков, пользователей или программ. Почему в Windows не используется разделитель :? Потому что он используется в названии диска (C:, D:) и таким образом входит в состав путей к файлам и каталогам.

Можно ли было обойтись без переменных окружения и заменить их другим механизмом передачи данных? Можно, но это только усложнит систему. Переменные окружения – это простой и универсальный способ передачи информации от системы к программам и одних программ к другим. Они просты не только для программной обработки, но и доступны пользователю.

Управление переменными из командной строки#

Командная оболочка bash дает удобный способ работы с переменными. Рассмотрим три группы команд:

  • просмотр;

  • установку;

  • удаление.

Команда printenv выводит на экран переменные окружения. Ее вызов без аргументов выведет весь список, где каждая переменная выводится с новой строки в формате name=value. Если передать ей в качестве аргумента имя переменной, то она выведет ее значение.

Вывод переменной чаще осуществляют командой echo, которая выводит на экран строки, переданные ей в качестве аргумента. Чтобы строка с именем переменной превратилась в значение, ее предваряют символом $. Символ $ указывает оболочке bash интерпретировать последующие символы как имя переменной и заменить их значением этой переменной.

echo PATH=$PATH

Note

Оболочка bash предлагает автоподстановку имен переменных, позволяющий ускорить ввод данных в терминале. Если имя однозначно определяется по первым набранным символам, то нажав на клавишу Tab вы допишете оставшуюся часть имени. Если набранным символам соответствует несколько имен, то второе нажатие на клавишу Tab покажет остальные варианты.

skt@home:~/MyProjects/sppo$ echo $GNOME_[Tab]
$GNOME_DESKTOP_SESSION_ID  $GNOME_TERMINAL_SCREEN
$GNOME_SHELL_SESSION_MODE  $GNOME_TERMINAL_SERVICE
skt@home:~/MyProjects/sppo$ echo $GNOME_

Команда export var=value создает новую или перезаписывает старую переменную var со значением value. Если значение содержит пробельный символ, то строка обрамляется кавычками: var="My files".

Warning

Обратите внимание, что присваивание не обрамлено пробелами. В противном случае команда не сработает.

Переменная окружения может хранить не только отдельное значение, но и список. В списке значения друг от друга разделяются символом :. Разделяющий символ не может быть использован в значениях переменной. Переменная PATH содержит список каталогов с исполняемыми файлами. Часто приходится добавить в нее новый каталог. Это возможно сделать командой export PATH=$PATH:new/path. Здесь также показано, что значения существующих переменных можно использовать для формирования новых.

Команда unset var удаляет переменную var. Удаление системной переменной не приведет к серьезным проблемам, так как в любой момент их можно вернуть в начальное состояние, перезапустив оболочку. Это значит, что рассмотренные выше команды, изменяют список переменных только у текущего процесса. С его завершением изменения в переменных исчезают.

Переменные окружения изменяются в терминале и передаются терминальным процессом, запускаемым с помощью него пользовательским процессам. Запускаемая программа наследует от родительского процесса его переменные окружения. Родительским процессом может быть ОС, командная оболочка или любая другая запущенная программа. После завершения программы, ее переменные окружения также теряются. Поэтому все изменения переменных окружения, которые были проведены в командной оболочке, после ее завершения, будут утеряны.

Установленная переменная окружения в терминале будет там находится до следующего перезапуска или явного удаления. В последующем она будет передана всем запускаемым программам, засоряя таким образом пространство и, возможно, приведет к побочным эффектам. Переменную можно передать явно только одной программе, и она исчезнет после этого, если добавить инициализацию переменной перед именем программы в той же строке: var=name program.

Инициализация переменных системой#

Переменные окружения система берет из конфигурационных файлов. Система добавляет свои переменные в файл /etc/environment. Системно значимые программы (интерпретатор bash, окружение рабочего стола unity, оконная система X Server и др.) добавляют в систему свои переменные, которые прописаны в их файлах настроек. Для bash – это /etc/profile и /etc/bashrc. Пользователь может определить собственные переменные окружения. Эти переменные прописываются в файлах настроек в домашнем каталоге. Интерпретатор bash использует для этого файл ~/.bashrc.

Добавление переменных. Если пользователю нужны специальные настройки для запускаемых им программ, он может их записать как переменные окружения в файлах сценариев bash. При запуске bash, они будут добавлены к списку доступных переменных.

Примеры использования переменных окружения#

В параллельных программах следует воздержаться от фиксации в исходном коде количества используемых потоков. Вместо этого число следует вынести в интерфейс и предоставить пользователю самостоятельно влиять на степень загрузки системы. В программах, распараллеленных технологией OpenMP, количество потоков в параллельных секциях берется из переменной окружения OMP_NUM_THREADS. Запуск программы program на четырех потоках осуществляется командой OMP_NUM_THREADS=4 ./program.

Переменная HOME указывает путь к домашнему каталогу, а USER – имя пользователя. Это позволяет персонализировать программу. Она часто используется в сценариях для указания каталога установки --prefix=$HOME. При установке файлы собранной программы скопируются в домашний каталог, не повлияв на других пользователей. При развертывании программ на компьютере установщик предлагает добавить каталог с программой к значению системной переменной окружения PATH. Это позволяет запускать программу из командной строки по имени, не указывая полный путь к исполняемому файлу.

Отладка программы на стороне пользователя представляет серьезную проблему для разработчика в связи с тем, что не доступна среда разработки, отладочные символы и не получится изучить внутреннее состояние программы. Один из приемов увидеть состояние – это воспользоваться отладочным выводом, активируемым, например, переменной DEBUG_OUTPUT=ON. Программа, заметив, что установлена переменная заранее оговоренным именем, сбрасывает в файл свое промежуточное состояние. Действие переменной на программу можно ограничить во времени, если инициализировать ее меткой – временем действия, как, например, DEBUG_OUTPUT=2022_11_30. Это позволит защититься от случая, когда разработчик забудет сбросить переменную на пользовательской машине, что повлияет на производительность программы.

Утилита /bin/pwd выводит на экран текущий каталог. Запущенная из разных каталогов, она выведет разный результат. Из какого источника утилита узнает путь к текущему каталогу? Текущий каталог программы хранится в переменной PWD, которую меняет команда cd. Утилита pwd, запускаясь оболочкой, наследует все ее переменные вместе с PWD. А как команда cd влияет на переменную PWD оболочки, если запущенный процесс не имеет доступа к переменным родительского процесса? Команда cd не может этого делать, если была бы внешней утилитой. Поэтому она является встроенной в bash командой – реализована в виде функции, которая меняет переменную окружения у своей программы.

Вопросы для контроля#

  1. Что такое переменная окружения?

  2. Какие переменные окружения вам известны?

  3. Откуда система берет переменные окружения?

Упражнения#

  1. Установите для текущей сессии новую переменную окружения ABC_ENVIRONMENT в значение local, отобразите ее на экране и удалите затем.

  2. Как передать программе переменную окружения DEBUG_OUTPUT=ON так, чтобы она была доступна только этой программе, но не следующим запускаемым.

  3. Добавьте переменную окружения LOCAL_SDK, доступную вашему пользователю при входе в систему. В какое значение?

  4. Добавьте к переменной PATH каталог /home/student/bin.

  5. Измените текущий каталог терминала, переопределив переменную PWD.

  6. Удалите переменную PATH. Попробуйте выполнить команду ls. Почему так происходит? Обратите изменение. Почему команда exit сработала?

  7. Установите переменной MYLIST список из значений value1, value2 и value3. Затем удалите из списка значение value2.

  8. Поменяйте цвет каталогов в выводе команды ls с темно-синего на более контрастное. Для этого обратите на переменную окружения LS_COLORS. Воспользуйтесь следующими подсказками: каталог на английский переводится как “directory”; дублирующий элемент в списке переопределяет предыдущий; если в аргументе есть символы, интерпретируемые как специальные, то такой аргумент следует обрамить кавычками.