[Перевод] Запуск Bash в деталях

Если вы нашли эту страницу в поиске, то наверняка пытаетесь решить какую-то проблему с запуском bash.

Возможно, в вашем окружении bash не устанавливается переменная среды и вы не понимаете, почему. Возможно, вы засунули что-то в различные загрузочные файлы bash или в профили, или во все файлы наугад, пока это не сработало.

В любом случае, смысл этой заметки — как можно проще изложить процедуру запуска bash, чтобы вы могли справиться с проблемами.

Диаграмма


Эта блок-схема обобщает все процессы при запуске bash.

c6ddca8846f8c34fd80e003c91c4356b.png

Теперь подробнее рассмотрим каждую часть.

Login Shell?


Сперва нужно выбрать, находитесь вы в командной оболочке входа (login shell) или нет.

Оболочка входа — это первая оболочка, в которую вы попадаете при входе в систему для интерактивного сеанса. Оболочка входа не требует ввода имени пользователя и пароля. Вы можете форсировать запуск оболочки входа, добавив флаг --login при вызове bash, например:

bash --login


Оболочка входа настраивает базовую среду при первом запуске оболочки bash.

Интерактивный?


Затем вы определяете, является оболочка интерактивной или нет.

Это можно проверить по наличию переменной PS1 (она устанавливает функцию ввода команд):

if [ "${PS1-}" ]; then
  echo interactive
else
  echo non-interactive
fi


Или посмотреть, установлен ли параметр -i, с помощью специальной переменной дефиса - в bash, например:

$ echo $-


Если в выдаче есть символ i, то оболочка является интерактивной.

В оболочке входа?


Если вы находитесь в оболочке входа, то bash ищет файл /etc/profile и запускает, если он существует.

Затем ищет любой из этих трёх файлов в следующем порядке:

~/.bash_profile
~/.bash_login
~/.profile


Когда находит один, то запускает его и пропускает другие.

В интерактивной оболочке?


Если вы находитесь в интерактивной оболочке без входа в систему (non-login shell), предполагается, что вы уже побывали в оболочке входа, окружение настроено и будет унаследовано.

В этом случае выполняются по порядку следующие два файла, если они существуют:

/etc/bash.bashrc
~/.bashrc


Ни один вариант?


Если вы не находитесь ни в оболочке входа, ни в интерактивной оболочке, то ваше окружение действительно будет пустым. Это вызывает большую путаницу (см. ниже о заданиях cron).

В этом случае bash смотрит на переменную BASH_ENV вашей среды и создаёт соответствующий файл, который там указан.

Типичные трудности и эмпирические правила


Задания cron


У меня в 95% случаев отладка запуска bash связана с тем, что задание cron работает не так, как ожидалось.

Эта проклятая задача работает нормально, когда я запускаю её в командной строке, но фейлится при запуске в crontab.

Здесь две причины:

  • Задания cron не являются интерактивными.
  • В отличие от скриптов в командной строке, задания cron не наследуют среду оболочки.


Обычно вы не замечаете или не заботитесь о том, что скрипт оболочки не является интерактивным, потому что среда наследуется от интерактивной оболочки. Это означает, что все PATH и alias настроены так, как вы ожидаете.

Вот почему зачастую приходится установить конкретный PATH для задачи cron, как здесь:

* * * * * PATH=${PATH}:/path/to/my/program/folder myprogram


Скрипты, вызывающие друг друга


Ещё одна распространенная проблема, когда скрипты по ошибке настроены на вызов друг друга. Например, /etc/profile обращается к ~/.bashrc.

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

Образ Docker в песочнице


Чтобы поэкспериментировать с запуском оболочки, я создал образ Docker, который можно использовать для отладки запуска оболочки в безопасной среде.

Запуск:

$ docker run -n bs -d imiell/bash_startup
$ docker exec -ti bs bash


Dockerfile находится здесь.

Для принудительного логина и имитации оболочки входа:

$ bash --login


Для проверки набора переменных BASH_ENV:

$ env | grep BASH_ENV


Для отладки crontab каждую минуту выполнятся простой скрипт (в /root/ascript):

$ crontab -l
$ cat /var/log/script.log

© Habrahabr.ru