Каждый раз, когда ты входишь по SSH или открываешь терминал, оболочка читает набор файлов и собирает себе окружение: где искать команды, как выглядит приглашение, какие сокращения работают. Если ты этого не понимаешь, то получаешь классику: alias работает в интерактивной сессии, но ломается в скрипте; PATH потерялся под cron; PROMPT свой у root и чужой у нового пользователя. В этом уроке разбираем, какие файлы и в каком порядке читает bash, чем login-сессия отличается от non-login, как объявлять переменные, псевдонимы и функции и как раздать всё это новым пользователям через /etc/skel.

Как это работает
У процесса есть два разных набора переменных. Переменные оболочки видит только сам bash, а переменные окружения наследуют дочерние процессы. Команда export помечает переменную как экспортируемую: после этого она попадает в окружение и её увидит любая запущенная из оболочки программа. Без export переменная остаётся локальной для текущего процесса bash и до потомков не дойдёт - это первая причина, почему скрипт не видит то, что ты задал в терминале.
Bash различает сессии по двум осям. Первая: login или non-login. Login-shell - это первый процесс при входе в систему (консоль tty, ssh, su с дефисом). Non-login - это терминал, открытый внутри уже залогиненной графической сессии, или просто запуск bash вручную. Вторая ось: интерактивная (есть приглашение, ты печатаешь команды) или нет (выполняется скрипт). От этой комбинации зависит, какие файлы прочитаются.
Login-shell читает сначала /etc/profile (а тот через цикл подтягивает все *.sh из /etc/profile.d), затем первый найденный из ~/.bash_profile, ~/.bash_login, ~/.profile - именно первый, остальные игнорируются. Interactive non-login shell читает /etc/bash.bashrc (в Debian/Ubuntu; в RHEL аналог собирается из /etc/bashrc, вызываемого из профиля) и ~/.bashrc. Поэтому в большинстве систем ~/.bash_profile содержит строчку, которая вручную подтягивает ~/.bashrc - чтобы при login-входе тоже сработали алиасы и функции из bashrc. При выходе из login-сессии bash читает ~/.bash_logout.
Псевдоним (alias) - это простая текстовая подстановка первого слова команды, без аргументов и логики. Функция bash мощнее: она принимает позиционные параметры $1, $2, имеет тело из нескольких команд и возвращает код через return. И alias, и функции живут только в текущей оболочке и в дочерние процессы НЕ наследуются (экспорт функции через export -f - редкое исключение). Поэтому их место - в ~/.bashrc, который перечитывается для каждой новой интерактивной оболочки.
Команда source (она же точка - точка пробел имя_файла) выполняет файл в ТЕКУЩЕЙ оболочке, а не в дочерней. Это принципиально: обычный запуск скрипта порождает новый bash, и все заданные там переменные и функции умирают вместе с ним. А source применяет изменения к твоей живой сессии - именно так перечитывают ~/.bashrc после правки, не разлогиниваясь.
Команды и примеры
Переменные, экспорт и просмотр окружения:
Код: Выделить всё
MYVAR=hello # переменная оболочки, потомки её не видят
export MYVAR # теперь это переменная окружения
export EDITOR=vim # объявить и экспортировать сразу
echo "$MYVAR"
env | grep MYVAR # env показывает ТОЛЬКО экспортированные
set | grep MYVAR # set показывает все переменные и функции оболочки
unset MYVAR # удалить
printenv PATH # одна переменная окруженияКод: Выделить всё
export PATH="$PATH:$HOME/bin" # дописать в конец, не затирая староеКод: Выделить всё
alias ll='ls -lah --color=auto'
alias # список всех алиасов
alias gs='git status'
\ls # обратный слэш игнорирует алиас разово
unalias ll # снять один
unalias -a # снять всеКод: Выделить всё
mkcd() {
mkdir -p -- "$1" && cd -- "$1"
}
mkcd ~/projects/test # создать каталог и сразу войтиКод: Выделить всё
source ~/.bashrc
. ~/.bashrcКод: Выделить всё
# и Debian, и RHEL читают это при login-входе:
sudo tee /etc/profile.d/mycorp.sh >/dev/null <<'EOF'
export CORP_REGION=eu
umask 027
EOF
# применится при следующем login; проверить сейчас:
source /etc/profile.d/mycorp.shКод: Выделить всё
ls -la /etc/skel # обычно .bashrc .bash_profile .profile
sudo cp /etc/skel/.bashrc /etc/skel/.bashrc.bak
# правим /etc/skel/.bashrc - добавляем корпоративные алиасы
sudo useradd -m -s /bin/bash anna # -m копирует /etc/skel в /home/anna
# уже существующим пользователям skel задним числом НЕ применяется- Поправил ~/.bashrc, а под ssh ничего не изменилось - потому что ssh даёт login-shell, который читает ~/.bash_profile, а тот не делает source ~/.bashrc. Добавь в ~/.bash_profile строку: if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
- Переменная работает в терминале, но не видна программе - забыл export. Без него потомки переменную не наследуют.
- PATH="$HOME/bin" без $PATH в начале или конце затирает системные пути - и перестают находиться обычные команды. Всегда дописывай, а не присваивай с нуля.
- Алиас не работает в скрипте: неинтерактивные оболочки по умолчанию не разворачивают алиасы, да и ~/.bashrc для них не читается. В скриптах используй функции, а не alias.
- Запустил конфиг как ./.bashrc вместо source - изменения ушли в дочерний bash и пропали. Перечитывать только через source или точку.
- Поправил /etc/skel и ждёшь, что у старых пользователей всё обновится - нет, skel копируется ОДИН раз при создании. Существующим раздавай вручную или через /etc/profile.d.
- export PATH в ~/.bashrc вместо ~/.bash_profile приводит к дублированию пути при каждом запуске вложенной оболочки - PATH-присваивания держи в profile, интерактивные настройки в bashrc.
- Выполни echo $$ и сравни вывод env и set по своей тестовой переменной: задай TEST=1 без export, проверь, что env её не показывает, потом export TEST и проверь снова.
- Открой два терминала: один по ssh (login), другой - в графической сессии (non-login). В каждом выполни shopt -q login_shell и сравни код возврата (echo $?).
- Добавь в ~/.bashrc алиас и функцию mkcd из урока, выполни source ~/.bashrc, убедись, что они работают.
- Создай /etc/profile.d/lab.sh с export LAB_OK=yes, разлогинься и зайди заново - проверь printenv LAB_OK.
- Сравни /etc/skel/.bashrc с твоим ~/.bashrc через diff и пойми, что ты накрутил поверх шаблона.
- Создай пользователя useradd -m testskel, зайди под ним (su - testskel) и проверь, что файлы из skel на месте.
- Чем переменная оболочки отличается от переменной окружения и какая команда превращает первую во вторую?
- Какие файлы и в каком порядке читает interactive login-shell, а какие - interactive non-login shell?
- Почему правка ~/.bashrc может не подействовать при входе по ssh и как это чинят?
- В чём разница между запуском скрипта как ./script.sh и через source script.sh?
- Когда содержимое /etc/skel попадает в домашний каталог пользователя и применяется ли оно к уже существующим аккаунтам?
- Почему alias не стоит использовать в скриптах и чем его заменить?