Переменные, PATH и окружение процесса
Приложение запускается из вашего терминала, но не работает из другого окна или служебного менеджера. Код одинаковый, а настройки отличаются. Один процесс знает адрес сервера из переменной, другой не получил её; в одном PATH найден нужный Python, в другом выбрана иная программа.
В прошлом уроке пакетный менеджер установил файлы. Теперь разберём, как shell находит программу и какие данные получает её новый процесс. В опытах не используем настоящие токены, не печатаем все секреты окружения и не переписываем глобальные startup-файлы. Для небольшой лаборатории достаточно двадцати минут.
Переменная оболочки
course_label='Linux practice'
printf '%s\n' "$course_label"
bash -c 'printf "child=<%s>\n" "$course_label"'В первом выводе будет Linux practice, в дочернем Bash обычно пустое значение. Переменная существует в родительском shell, но не экспортирована новому процессу. Одинарные кавычки во второй команде сохраняют текст для разбора дочерней оболочкой; иначе родитель мог подставить значение ещё до запуска.
Присваивание не содержит пробелов вокруг =. course_label = value становится иной командной строкой, а не установкой переменной. Кавычки защищают пробел внутри самого значения. Переменные shell хранят текст; число в них не приобретает автоматически строгий целочисленный тип приложения.
Экспорт и наследование
export course_label
bash -c 'printf "child=<%s>\n" "$course_label"'
bash -c 'course_label=changed'
printf '%s\n' "$course_label"Теперь ребёнок получает значение, но изменение внутри ребёнка не переписывает окружение родителя. Новое исполнение наследует копию окружения при запуске. Позднейший export также не меняет уже работающий сервис. Это объясняет необходимость отдельного перезапуска или предусмотренного приложением reload.
Для одной команды настройку можно ограничить её запуском:
COURSE_MODE=demo bash -c 'printf "mode=%s\n" "$COURSE_MODE"'
printf 'parent mode=<%s>\n' "${COURSE_MODE-}"При ранее неустановленной переменной родительский вывод пустой. ${NAME-} позволяет подставить пустое значение, если NAME отсутствует. Такой опыт не меняет все будущие программы. Не используйте учебные переменные $HOME, $PATH или другие системные имена для собственного случайного счётчика.
Как ищется команда
PATH — список каталогов, разделённых двоеточиями. Когда вы вводите имя без /, shell ищет подходящую команду согласно своим правилам, включая builtins, aliases и внешние файлы. Путь с /, например ./report.sh, уже задаёт место и не ищется обычным обходом PATH.
printf '%s\n' "$PATH"
type python3
command -v python3Не разбирайте вывод PATH как список файлов с пробелами. Порядок каталогов имеет значение: программа в первом подходящем месте может скрыть другую с тем же именем. Добавление текущего каталога . в начало PATH позволяет случайно выполнить чужой файл вместо известного инструмента. Для первого курса используйте явный ./ у своего скрипта.
Программа, запущенная службой, не обязана наследовать PATH вашего интерактивного Bash. Поэтому позже в unit-файле укажем абсолютный путь к интерпретатору. Путь /usr/bin/python3 в Ubuntu нужно подтвердить через command -v, а не считать единственно возможным для всех машин.
Временная настройка и startup-файл
Новый терминал создаёт новый сеанс. Одни файлы читаются для интерактивной оболочки, другие — для login-shell; правила Bash и zsh различаются. .bashrc не является универсальным источником настроек каждого процесса. Systemd, cron и другой запуск могут его вообще не читать.
Перед постоянным изменением удобно проверить настройку одной команды. Если всё-таки редактируете свой startup-файл, сначала сделайте копию, добавляйте небольшое изменение и проверьте новый сеанс. Для завершения этой лаборатории это не требуется. Alias удобен человеку, но не должен незаметно определять смысл автоматического скрипта.
Секрет в переменной окружения не становится автоматически защищённым хранилищем. Программа может вывести его в лог, дамп или диагностику; другие полномочия позволяют наблюдать часть состояния. Команда env печатает окружение, поэтому её полный вывод не отправляют в публичный issue без проверки. Здесь используем только явно учебные значения.
Рабочий каталог тоже настройка
Процесс наследует не только окружение, но и текущий каталог. config.txt ищется относительно него. Файл может существовать рядом с исходником, но программа, запущенная из другого места, его не найдёт. Надёжный запуск задаёт рабочий каталог либо передаёт абсолютный путь. Не лечите такой случай копированием конфигурации во все папки.
Самостоятельная лаборатория
Создайте переменную без export, проверьте ребёнка, затем экспортируйте и проверьте снова. Измените её в ребёнке и докажите сохранение родительского значения. Запустите один процесс с COURSE_MODE и другой без него. Наконец, найдите выбранный Python и запишите свой текущий каталог.
Критерии готовности: вы предсказываете четыре вывода до исполнения, не меняете системный PATH глобально и не публикуете настоящие credentials. Для неожиданного результата проверяете, не была ли переменная уже установлена в исходном сеансе. Наблюдение хранится вместе с условиями.
Разбор
Неэкспортированное значение остаётся только в shell, экспортированное становится частью окружения нового процесса. Родитель не получает обратной автоматической записи. Значение перед конкретной командой ограничено её запуском. Совпадение имени программы не гарантирует одинаковый файл при разном PATH. Следующий урок свяжет эти правила в маленький воспроизводимый скрипт.
Первичные источники: GNU Bash: environment, command search, startup files.