Что используют программы для чтения своей среды?

Вопрос или проблема

Я использую Fedora, где все предустановленные оболочки, по-видимому, поддерживают bashism: bash --posix работает, и даже sh. Тем не менее, когда я использовал эту функцию,

pathprepend () {
    if [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="$1${PATH:+":$PATH"}"
    fi
}

чтобы добавить каталог в PATH из ~/.profile, Git его не увидел. Обратите внимание, что echo $PATH в bash-сессии, из которой я использовал Git, показал мой каталог, я заметил это просто потому, что использую diff-highlight, связанный символической ссылкой в этом каталоге, и после того как я начал использовать эту функцию pathprepend, Git жаловался, что diff-highlight отсутствует. После восстановления POSIX-соответствующей настройки PATH всё вернулось к обычному функционированию.

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


Редактирование

@Bodo, вот что я использовал раньше: в ~/.profile,

pathprepend () {
    if [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="$1${PATH:+":$PATH"}"
    fi
}

pathprepend "$HOME/.local/bin"

Под “POSIX-соответствующий” я имею в виду без bashism. Вот что у меня сейчас в ~/.profile:

pathprepend () {
  case ":$PATH:" in
    *":$1:"*)
      :;;
    *)
      PATH="$1${PATH:+":$PATH"}";;
  esac
}

pathprepend "$HOME/.local/bin"

В обоих случаях ~/.profile считывается из ~/.bash_profile, который содержит

# Загружаем конфигурацию для логин-сессий любых оболочек
if [[ -f "$HOME/.profile" ]]; then
  source "$HOME/.profile"
else
  echo >&2 "$HOME/.bash_profile: $HOME/.profile не найден"
fi

# Загружаем конфигурацию для интерактивных нелогиновых сессий Bash
case "$-" in *i*)
  if [[ -f "$HOME/.bashrc" ]]; then
    source "$HOME/.bashrc"
  else
    echo >&2 "$HOME/.bash_profile: $HOME/.bashrc не найден"
  fi;;
esac

Я не использовал export для PATH, потому что Гордон Дэвисон объясняет в ответе, из которого я скопировал функцию, что “PATH уже должен быть маркирован как экспортированный, поэтому его повторная экспортировка не нужна.” Фактически, я до сих пор не использую export, но с версией case функции pathprepend всё работает нормально. Кстати, какой родительский процесс у процесса Git, является ли это Bash-процесс, из которого я использую git?

программы получают свою среду из третьего аргумента системного вызова execve(program_path, argv, envp), который их выполняет.

envp, как и argv, представляет собой массив строк, за исключением того, что для envp по соглашению строки имеют формат var=value.

Что программы делают с этими строками, решают они сами, но в общем случае они берут эти строки var=value и интерпретируют то, что слева от первого =, как имя переменной среды, а то, что справа, как её значение.

Обычно они сохраняют этот список и когда они, в этом или дочерних процессах, выполняют другие команды, они передают этот же список в третьем аргументе соответствующему системному вызову execve().

В библиотеке C есть вспомогательные функции для этого. Этот сохраненный список — переменная environ, и функции putenv()/setenv()/unsetenv() можно использовать для добавления/изменения/удаления переменных там, а функции типа execlp() являются обёртками для системного вызова execve(), которые передают этот environ автоматически как envp. Идея заключается в том, что среда предназначена для наследования автоматическими путями через выполнение¹.

Большинство оболочек сопоставляют переменные среды с переменными оболочки.

В оболочках, подобных POSIX, переменные из envp, которые имеют имя, совместимое с именем переменной оболочки, преобразуются в переменную оболочки, которая маркируется атрибутом export. Также специальная встроенная утилита export может использоваться для повышения статуса переменной оболочки до переменной среды, чтобы она передавалась в envp каждой последующей выполняемой команде.

~/.profile — это файл инициализации сессии, который интерпретируется большинством оболочек, подобных Bourne, когда они вызваны как логиновые оболочки (что login делает, добавляя - к argv[0]). Эквивалентом для оболочек, подобных csh, будет ~/.login, также смотрите ~/.zprofile/~/.zlogin для zsh.

На сегодняшний день это обычно считывается при входе через ssh (при использовании только ssh host, а не ssh host 'shell code'), в виртуальном терминале или sudo -i, редко терминальными эмуляторами, запущенными в графической сессии входа в систему.

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

Если при входе в систему ваша логиновая оболочка была запущена как логиновая оболочка и она подобна Bourne (и не zsh), она интерпретировала код в ~/.profile.

При интерпретации PATH="~/.local/bin${PATH:+":$PATH"}" было обновлено $PATH переменная оболочки. Поскольку эта переменная будет найдена в envp, переданной этой оболочке, эта переменная будет отмечена для экспорта, и переменная с её изменённым значением² будет включена в envp, переданный каждой команде, выполняемой впоследствии, включая git. Если git выполнит getenv("PATH"), он должен получить это изменённое значение.

Но для этого ~/.profile должен быть интерпретирован оболочкой в иерархии процессов, которая выполняет git.


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

² За исключением оболочки Bourne, где изменение переменной оболочки не отражается на переменной среды, если не вызвать export. Но вы не найдёте оболочку Bourne в Fedora или в любой современной системе.

.

Ответ или решение

Вопрос, который мы рассматриваем, касается того, как программы получают доступ к своему окружению, и почему иногда изменения в переменных окружения не отражаются, как ожидалось. Данная задача относится к технологиям и операционным системам, особенно в контексте использования оболочки Bash и системных вызовов в операционной системе Fedora. Попробуем разобраться в этом вопросе более детально.

Теория

В Unix-подобных системах, таких как Fedora, процессы наследуют свое окружение от родительского процесса. Информация об окружении передаётся процессам через системный вызов execve. Этот вызов принимает три параметра: путь к исполняемому файлу, массив аргументов командной строки и массив переменных окружения (envp). Каждая запись в массиве envp представляет собой строку формата var=value, где var — это имя переменной, а value — ее значение.

Программы могут работать с этой коллекцией окружения по-разному. Обычно они распознают строки var=value и интерпретируют var как имя переменной окружения, а value как ее значение. При запуске дочерних процессов программы обычно передают список окружения следующему процессу с использованием того же вызова execve().

Пример

В контексте вопроса, проблема возникает при добавлении нового пути в переменную окружения PATH через пользовательскую функцию pathprepend в файле ~/.profile. Однако Git, который запускается из этой Bash-сессии, не видит изменений в PATH. Это может быть связано с тем, как и когда интерпретируются стартовые файлы окружения.

Для именования нескольких критически важных моментов:

  1. Инициализация окружения: ~/.profile обычно выполняется при входе в систему для настроек сессии в интерактивной оболочке (например, при входе через SSH). Тем не менее, терминальные эмуляторы, используемые в графической среде, такие как GNOME Terminal, могут не инициировать оболочку входа (login shell), что означает, что ~/.profile может быть не выполнен.

  2. Оболочка и экспорт переменных: При вызове в ~/.bash_profile и ~/.profile, переменная PATH устанавливается и наследуется процессами, запуск которых инициирован из этой же оболочки. Если PATH экспортирован, то изменения должны быть видны всем дочерним процессам этой сессии.

  3. Влияние POSIX-соответствия: Использование POSIX-соответствующего стиля в скриптах может влиять на то, как интерпретируются команды и как производится экспорт переменных. Проблемы возникают, если используются команды или функции, не соответствующие POSIX в строго POSIX-режиме.

Применение

Для гарантии, что изменения в PATH видны всем процессам, рекомендовано следующее:

  • Убедитесь, что оболочка работает в режиме login shell при запуске терминала, который вы используете. Это может требовать настройки терминального эмулятора для запуска оболочки как login shell. В GNOME Terminal, например, это можно настроить в параметрах.

  • Всегда проверяйте, удовлетворяют ли ваши скрипты и команды POSIX-стандарту, если вы хотите избежать проблем с совместимостью в других средах или профессионально настроенных системах.

  • Для графической среды рекомендуется использовать файлы ~/.bashrc или системы управления сессиями, такие как environment.d от systemd, для задания переменных окружения, которые будут применяться во всех случаях, включая неинтерактивные и интерактивные сессии.

Чтобы вся система функционировала как положено, важно понимать, каким образом Unix-подобные системы управляют информацией об окружении и как различные обертки и утилиты, такие как оболочки и скрипты инициализации, взаимодействуют друг с другом в этом процессе. Эти знания помогут в эффективной настройке и устранении возникающих сбоев в поведении программ.

Оцените материал
Добавить комментарий

Капча загружается...