Назначение [-n «$ PS1»] в bashrc

10

Какая цель делает [ -n "$PS1" ]в [ -n "$PS1" ] && source ~/.bash_profile;служите? Эта линия входит в .bashrcиз точечных файлов репо .

shotes
источник

Ответы:

20

Это проверяет, является ли оболочка интерактивной или нет. В этом случае, только поиск ~/.bash_profileфайла, если оболочка является интерактивной.

Смотрите "Является ли эта оболочка интерактивной?" в руководстве по bash, которое цитирует эту специфическую идиому. (Также рекомендуется проверить, является ли оболочка интерактивной, проверив, $-содержит ли специальная переменная iсимвол, что является лучшим подходом к этой проблеме.)

filbranden
источник
bash, по крайней мере, отключит PS1 и PS2, если оболочка является интерактивной . Вы можете видеть это для себя, с ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' )которой просто печатает []. Кажется , ЗШ не делает то же самое, по крайней мере , из эксперимента ... В любом случае, намерение из [ -n "$PS1" ], чтобы проверить , является ли интерактивной оболочкой или нет.
filbranden
3
Это bashсбрасывает PS1, когда неинтерактивный (опечатка в вашем предыдущем комментарии) является ошибкой IMO, PS1 не является специфичной для bash переменной, он не имеет смысла ее сбрасывать. Это единственная оболочка, которая делает это (хотя yashтакже устанавливает PS1значение по умолчанию, даже когда не является интерактивным).
Стефан Шазелас
1
Поскольку рассматриваемый код находится в специфичном для bash файле, это кажется разумным ответом. Другие ответы касаются более общего случая спецификаций POSIX или других оболочек. Вы ответили: «Какова цель этого?» намерение в вопросе, оставляя в стороне все остальное. Приятно знать, что делает Bash и, возможно, лучшие способы достижения цели.
Джефф Шаллер
Я бы посчитал этот ответ более полным, если бы он предложил более надежную альтернативу (скажем, [[ $- = *i* ]] && source ~/.bash_profile).
Чарльз Даффи
@CharlesDuffy Честно говоря, я не думаю, что здесь что-то не так [ -n "${PS1}" ], но я все же обновил свой ответ, чтобы подчеркнуть, что руководство по bash также предлагает / рекомендует проверять, $-является ли оболочка интерактивной, надеюсь, вы найдете, что это улучшает ответ. Ура!
filbranden
19

Что это делает

Это широко распространенный способ проверки, является ли оболочка интерактивной. Помните, что он работает только в bash, он не работает с другими оболочками. Так что это нормально (если глупо) для .bashrc, но это не сработает .profile(это читается sh, а bash - только одна из возможных реализаций sh, и не самая распространенная).

Почему это работает (только в bash!)

Интерактивная оболочка устанавливает переменную оболочкиPS1 в строку приглашения по умолчанию. Поэтому, если оболочка является интерактивной, PS1она установлена ​​(если только пользователь .bashrcне удалил ее, чего не могло быть еще наверху .bashrc, и вы могли бы подумать, что в любом случае это глупо).

Обратное утверждение верно для bash: неинтерактивные экземпляры bash сбрасываются PS1при запуске. Обратите внимание, что это поведение характерно для bash и, возможно, является ошибкой (почему бы bash -c '… do stuff with $var…'не работать, когда varесть PS1?). Но все версии bash вплоть до 4.4 (включая последнюю версию, как я пишу) делают это.

Многие системы экспортируются PS1в окружающую среду. Это плохая идея, потому что много разных оболочек используют, PS1но с другим синтаксисом (например , экранирование приглашений bash полностью отличается от экранирования приглашений zsh ). Но это достаточно широко распространено, так что на практике PS1его установка не является надежным индикатором интерактивности оболочки. Оболочка может быть унаследована PS1от среды.

Почему это (неправильно) используется здесь

.bashrcэто файл, который bash читает при запуске, когда он интерактивный. Менее известным фактом является то, что bash также читает .bashrcоболочку входа в систему, и эвристика bash заключает, что это удаленный сеанс (bash проверяет, является ли его родитель rshdили sshd). Во втором случае маловероятно, что PS1это будет установлено в среде, потому что ни один точечный файл еще не запущен.

Однако то, как код использует эту информацию, контрпродуктивно.

  • Если оболочка является интерактивной оболочкой, она запускается .bash_profileв этой оболочке. Но .bash_profileэто скрипт для входа в систему. Он может запускать некоторые программы, которые предназначены для запуска только один раз за сеанс. Это может переопределить некоторые переменные среды, которые пользователь преднамеренно установил на другое значение перед запуском этой оболочки. Запуск .bash_profileв оболочке без входа в систему разрушителен.
  • Если оболочка является неинтерактивной оболочкой удаленного входа, она не загружается .bash_profile. Но это тот случай, когда загрузка .bash_profileможет быть полезной, потому что неинтерактивная оболочка входа в систему не загружается автоматически /etc/profileи ~/.profile.

Я думаю, что причина, по которой люди делают это, - для пользователей, которые входят через GUI (очень распространенный случай) и которые .bash_profileвместо этого помещают свои переменные параметры среды .profile. Большинство механизмов входа в GUI вызывают, .profileно не вызывают .bash_profile(чтение .bash_profileпотребует запуска bash как части запуска сеанса, а не sh). При такой конфигурации, когда пользователь открывает терминал, он получает свои переменные среды. Однако пользователь не получит свои переменные среды в приложениях с графическим интерфейсом, что является очень распространенным источником путаницы. Решением здесь является использование .profileвместо .bash_profileустановки переменных среды. Добавление моста между .bashrcи .bash_profileсоздает больше проблем, чем решает.

Что делать вместо

Существует простой, портативный способ проверить, является ли текущая оболочка интерактивной: проверьте, -iвключена ли эта опция .

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Это полезно .bashrcдля чтения .profileтолько в том случае, если оболочка не является интерактивной, то есть противоположна тому, что делает код! Читайте, .profileесли bash является (неинтерактивной) оболочкой для входа, и не читайте ее, если это интерактивная оболочка.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Жиль "ТАК - перестань быть злым"
источник
4
Стоит отметить, что лучший способ проверить, является ли оболочка интерактивной, с помощью [[ -o interactive ]](ksh, bash, zsh) или case $- in (*i*) ...; esac(POSIX)
Стефан Шазелас
2
Мой bash (версия 4.4.12) фактически отключается, PS1если не работает в интерактивном режиме. Это достаточно просто проверить: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'ничего не печатает, а PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'печатает значение $PS1set в ваших файлах запуска bash (строка «кукушка» не печатается).
FooF
1
@ Стефан Шазелас: POSIX не требует, что $-содержит iс интерактивной оболочкой.
Шили
1
Bosh делает это с 2012 года, чтобы быть совместимым с ksh. Это просто не требовалось POSIX, пока указанное вами изменение не вступит в силу.
Шили
1
Честно говоря, я бы сказал, что [ -n "${PS1}" ] неправильный вызов заходит слишком далеко, ведь он ломается только тогда, когда кто-то экспортирует PS1 (что в своем ответе вы говорите, что это плохая идея, и даже вдумываетесь в причины), и это не влияет В любом случае, bash (поскольку он сбрасывает PS1 и PS2, если оболочка не является интерактивной.) Возможно, лучше использовать слово «обескураженный» или говорить об «ограничениях» подхода. Я не думаю, что это "неправильно" вообще. Если что-то не так экспортирует PS1, это точно! В любом случае, спасибо, что углубились в детали этого.
filbranden
1

Кажется, что эта странная концепция является результатом того факта, что bashона начиналась не как клон оболочки POSIX, а как Bourne Shellклон.

В результате, интерактивное поведение POSIX (которое $ENVвызывается для интерактивных оболочек) было добавлено позже bashи широко не известно.

Существует одна оболочка, которая обеспечивает подобное поведение. Это cshи гранты csh, которые $promptимеют конкретные значения:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Но это не относится ни к Bourne Shell, ни к POSIX-оболочкам.

Для оболочки POSIX единственный предоставленный метод - поместить код для интерактивных оболочек в файл:

$ENV

это имеет конкретное имя оболочки. Это например

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

Другие люди упоминали флаг оболочки -i, но он не пригоден для надежного программирования. POSIX не требует, чтобы это set -iработало и не $-содержало iинтерактивных оболочек. POSIX просто требует, sh -iчтобы оболочка переводилась в интерактивный режим.

Поскольку переменная $PS1может быть импортирована из среды, она может иметь значение даже в неинтерактивном режиме. Тот факт, что bash unsets PS1в любой неинтерактивной оболочке не предоставляется стандартом и не делается никакой другой оболочкой.

Таким образом, чистое программирование (даже с bash) состоит в том, чтобы поместить команды для интерактивных оболочек $HOME/.bashrc.

Шили
источник
0

Сначала я расскажу о том, что Debian, а также большую часть времени Ubuntu устанавливает для bash. И последнее касается других систем.

В настройках файлов запуска оболочки есть много мнений.
У меня тоже есть свое мнение, но я постараюсь показать существующие примеры правильных настроек.
Я буду использовать debuan, так как примеры его файлов довольно легко найти.
И Debian активно используется, поэтому настройки были хорошо протестированы,

Какова цель проверки, что PS1 установлен?

Только чтобы узнать, является ли оболочка интерактивной.

По умолчанию /etc/profileв Debian и Ubuntu (из / usr / share / base-files / profile):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

If читается: если интерактивный (PS1 установлен по умолчанию) и это оболочка bash (но не действует по умолчанию sh), то измените PS1 на новый (не по умолчанию).

Значение по умолчанию /etc/bash.bashrcв Debian также содержит:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Что довольно ясно в том, что он делает: если интерактивный не источник (остальное).

Тем не менее, в /etc/skel/.bashrcпример правильного пути к испытанию для интерактивной оболочки ( с помощью $-):

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Это должно ясно показать, почему PS1 и одна альтернатива.

Правильный порядок

Настройки, о которых вы сообщаете, следует избегать.
Порядок (от системных настроек и более конкретных пользовательских настроек (для Баш)) является /etc/profile, /etc/bash.bashrc, ~/.profileи , наконец ~/.bashrc. Это помещает самые широкие эффекты (и для большего количества оболочек) в /etc/profile(который принадлежит root), за которым следует /etc/bash.bashrc(который также принадлежит root), но влияет только на bash. Затем перейдите к личным настройкам $HOME, первый ~/.profileдля большинства оболочек и ~/.bashrc(почти эквивалентный ~/.bash_profile), специфичный только для bash.

Поэтому неправильно источник ~/.bashrcв ~/.profileэто трансформирует специфичный для установки Баша к более общим , что пользователю затрагивают более оболочки . За исключением случаев, когда сделано так :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Он проверяет, что bash работает, и загружается только .bashrcв этом случае.

Это исходное решение от Debian. Обоснование объясняется здесь .

На самом деле, обратный поиск ~/.profileв ~/.bash_profile(или ~/.bashrc) только повторяет применение общих правил, которые должны были быть уже загружены в конкретный вариант использования, и, следовательно, «не так уж плохо» (я не говорю «хорошо»). И я не говорю «хорошо», потому что это может вызвать зацикливание источников файлов. Например, когда подкаталог загружает родительский элемент, это цикл каталога.

И именно в этом кросс-поиске смысл проверки интерактивной оболочки имеет смысл. ~/.bashrcЗагружается только интерактивная оболочка , но она, в свою очередь, может загружаться ~/.profile(или наоборот), и в этом случае можно использовать проверку интерактивной оболочки.

Исаак
источник