Почему переменные, такие как $PS1, не указаны в printenv?

Из того, что я могу сказать printenv отображает переменные среды, но почему я не вижу другие переменные, такие как PS1 для настройки командной строки?

Что именно printenv вывод и почему не забрать PS1? Есть ли более полная команда вывода, которая делает больше, чем printenv?

2 ответа

Решение

Это потому что PS1 обычно не экспортируется.

Переменные среды используются для установки среды выполнения дочерних процессов; поскольку PS1 действительно имеет значение только в интерактивной оболочке, обычно ее не нужно экспортировать - это просто обычная переменная оболочки.

Если вы запустите интерактивную дочернюю оболочку, она прочитает и установит PS1 из файла ресурсов оболочки, таких как ~/.bashrc

если ты export PS1 тогда вы увидите это в printenv выход. В качестве альтернативы вы можете увидеть простые переменные оболочки, используя встроенную в bash set как описано здесь Как перечислить все имена переменных и их текущие значения?

Есть ли более полная команда вывода, которая делает больше, чем printenv?

printenv печатает только переменные окружения, что можно считать преимуществом. Но если вы хотите распечатать переменные оболочки, используйте echo "$x" (или же printf '%s\n' "$x", что является более надежным) вместо printenv x,

Объяснение SteelDriver по этим вопросам полезно и правильно, но я представляю эту тему по-другому здесь.

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


Переменные оболочки

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

Создание новой переменной, а затем выполнение внешней команды не передает переменную в командную среду. За исключением необычного случая, когда у вас уже есть переменная окружения x эта команда не выводит:

(x=foo; printenv x)

Переменная присваивается в оболочке. Эта команда выводит foo:

(x=foo; echo "$x")

Оболочка поддерживает синтаксис для передачи переменной в среду команды, не влияя на текущую среду оболочки. Это выводы foo:

x=foo printenv x

(Это работает в подрубрике, конечно же, (x=foo printenv x) - но я показал это без () потому что когда вы используете этот синтаксис, для вашей текущей оболочки ничего не устанавливается, поэтому нет необходимости использовать подоболочку для предотвращения влияния последующих команд.)

Это печатает foo, то печатает bar:

(x=bar; x=foo printenv x; echo "$x")

Экспорт

Когда вы экспортируете переменную, она автоматически передается в окружение всех последующих внешних команд, запускаемых из той же оболочки. export Команда делает это. Вы можете использовать его до того, как определите переменную, после того, как определите ее, или вы можете даже определить переменную в export сама команда Все эти печати foo:

(x=foo; export x; printenv x)
(export x; x=foo; printenv x)
(export x=foo; printenv x)

Здесь нет unexport команда. Несмотря на то, что вы можете экспортировать переменную перед ее установкой, сброс переменной также не экспортирует ее, то есть, что это ничего не печатает, а печатает bar:

(x=foo; export x; unset x; x=bar; printenv x)

Но изменение значения переменной после ее экспорта влияет на экспортируемое значение. Это печатает foo, затем bar:

(export x=foo; printenv x; x=bar; printenv x)

Как и другие процессы, ваша оболочка сама наследует переменные окружения от своего родительского процесса. Такие переменные изначально присутствуют в среде вашей оболочки и автоматически экспортируются - или остаются экспортированными, если вы решите думать об этом таким образом. Это печатает foo (Помните, VAR=val cmd работает cmd с VAR установлен в val в своей среде):

x=foo bash -c 'printenv x'

Переменные, заданные в дочерних процессах, не влияют на родительский процесс, даже если они экспортируются. Это печатает foo (не bar):

(x=foo; bash -c 'export x=bar'; echo "$x")

Подоболочки

Подоболочка также является дочерним процессом 2; это также печатает foo:

(x=foo; (export x=bar); echo "$x")

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

Тем не менее, подоболочки особенные. В отличие от других подпроцессов, таких как созданные при запуске внешней команды, такой как printenv или же bash подоболочка наследует большую часть состояния своей родительской оболочки. В частности, подоболочки наследуют даже переменные, которые не экспортируются. Как только (x=foo; echo "$x") печать foo так же (x=foo; (echo "$x")),

Неэкспортированная переменная все еще не экспортируется в подоболочке - если вы не экспортируете ее - так же, как (x=foo; printenv x) ничего не печатает, как и (x=foo; (printenv x)),

Подоболочка - это особый вид подпроцесса, который является оболочкой. Не все подпроцессы, которые являются оболочками, являются подоболочками. Оболочка, созданная путем запуска bash не является подоболочкой и не наследует неэкспортированные переменные. Так что эта команда печатает пустую строку (потому что echo печатает новую строку, даже если вызывается с пустым аргументом):

(x=foo; bash -c 'echo "$x"')

Зачем PS1 не является переменной окружения (и обычно не должно быть таковой)

Наконец, почему переменные типа PS1 являются переменными оболочки, но не переменными среды, причины:

  1. Они нужны только в оболочке, а не в других программах.
  2. Они установлены для каждой интерактивной оболочки, а неинтегративным оболочкам они вообще не нужны. То есть им не нужно наследоваться.
  3. Попытка пройти PS1 на новую оболочку, как правило, не получится, потому что оболочка обычно PS1 ,

Пункт № 3 заслуживает немного большего объяснения, хотя, если вы никогда не пытаетесь PS1 переменная окружения, тогда вам, вероятно, не нужно знать детали.

Когда Bash запускается неинтерактивно, он сбрасывает PS1,

Когда запускается неинтерактивная оболочка Bash, она всегда отключается PS1, Это печатает пустую строку (не foo):

PS1=foo bash -c 'echo "$PS1"'

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

PS1=foo bash -c 'if [[ -v PS1 ]]; then echo set; else echo unset; fi'

Чтобы убедиться, что это не зависит от другого поведения при запуске, вы можете попробовать передать любую комбинацию --login, --norc, или же --posix до -c или настройка BASH_ENV на пути какого-то сценария (например, BASH_ENV=~/.bashrc PS1=foo bash ...), или же ENV если ты прошел --posix, Ни в коем случае неинтерактивная оболочка Bash не может быть сброшена PS1,

Это означает, что если вы экспортируете PS1 и запустить неинтерактивную оболочку, которая сама запускает интерактивную оболочку, она не будет иметь PS1 значение, которое вы изначально установили. По этой причине - а также потому, что другие оболочки кроме Bash (например, Ksh) не все ведут себя одинаково, и как вы пишете PS1 потому что Баш не всегда работает для этих оболочек - я рекомендую не пытаться сделать PS1 переменная окружения. Просто отредактируйте ~/.bashrc установить любой запрос, который вы хотите.

Когда Bash запускается в интерактивном режиме, он часто устанавливает или сбрасывает PS1,

И наоборот, если вы отключили PS1 и запустить интерактивную оболочку Bash, даже если вы запретите ей запускать команды из сценариев запуска, передавая --norc, он все равно будет автоматически установлен PS1 к значению по умолчанию. Бег env -u PS1 bash --norc дает вам интерактивную оболочку Bash с PS1 установлен в \s-\v\$ , Так как Bash расширяется \s на имя оболочки и \v на номер версии, это показывает bash-4.3$ как подсказка на Ubuntu 16.04 LTS. Обратите внимание, что настройка PS1 Значение в виде пустой строки не совпадает с ее сбросом. Как объясняется ниже, работает PS1= bash дает вам интерактивную оболочку со странным поведением при запуске. Вы должны избегать экспорта PS1 когда он установлен на пустую строку, в практическом использовании, если вы не понимаете и не хотите такого поведения.

Однако, если вы установите PS1 и запустить интерактивную оболочку Bash - и она не будет сброшена промежуточной неинтерактивной оболочкой - она ​​сохранит это значение... до сценария запуска, подобного глобальному /etc/profile (для логинов) или /etc/bash.bashrcили ваш пользователь ~/.profile, ~/.bash_login, или же ~/.bash_profile (все для логинов) или ~/.bashrc сбрасывает его.

Даже если вы редактируете эти файлы, чтобы предотвратить их установку PS1 - что в случае /etc/profile а также /etc/bash.bashrc Я рекомендую не делать этого в любом случае, так как они влияют на всех пользователей - вы не можете действительно полагаться на это. Как упоминалось выше, интерактивные оболочки, созданные из неинтерактивных оболочек, не будут иметь PS1, если только вы не сбросили и не экспортировали его в неинтерактивной оболочке. Кроме того, вам следует дважды подумать, прежде чем делать это, потому что для шелл-кода (включая функции оболочки, которые вы, возможно, определили) обычно проверяют PS1 определить, является ли оболочка, в которой она работает, интерактивной или неинтерактивной.

проверка PS1 это общий способ определить, является ли текущая оболочка интерактивной.

Вот почему так важно, чтобы неинтерактивные оболочки Bash 4 отключались PS1 автоматически. Как в разделе 6.3.2 Является ли эта оболочка интерактивной? Справочник Bash гласит:

[S] скрипты tartup могут проверять переменную PS1; это не установлено в неинтерактивных оболочках и установлено в интерактивных оболочках.

Чтобы увидеть, как это работает, посмотрите пример там. Или ознакомьтесь с реальным использованием Ubuntu. По умолчанию, /etc/profile в Ubuntu входят:

if [ "$PS1" ]; then
  if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then
    # The file bash.bashrc already sets the default PS1.
    # PS1='\h:\w\$ '
    if [ -f /etc/bash.bashrc ]; then
      . /etc/bash.bashrc
    fi
  else
    if [ "`id -u`" -eq 0 ]; then
      PS1='# '
    else
      PS1='$ '
    fi
  fi
fi

/etc/bash.bashrc, который ничего не должен делать, когда оболочка не является интерактивной, имеет:

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

Тонкости разных методов проверки на интерактивность:

Чтобы достичь той же цели, /etc/skel/.bashrc, который копируется в домашние каталоги пользователей при создании их учетных записей (поэтому ваш ~/.bashrc вероятно, похоже), имеет:

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

Это еще один распространенный способ проверить, является ли оболочка интерактивной: посмотрите, получен ли текст путем расширения специального параметра. - (написав $-) содержит письмо i, Обычно это имеет точно такой же эффект. Предположим, однако, что вы не изменили приведенный выше код, который отображается по умолчанию в сценариях запуска Bash в Ubuntu, и что:

  1. вы экспортируете PS1 в качестве переменной среды, и
  2. это установлено, но к пустому значению, и
  3. вы запускаете интерактивную оболочку Bash...

затем /etc/profile (если это оболочка входа в систему) или /etc/bash.bashrc не будет запускать команды, которые они обычно запускают для интерактивных оболочек. ~/.bashrc все еще будет.

Если вы хотите проверить, является ли оболочка интерактивной, используя PS1 и получить правильный ответ, даже когда PS1 установлен, но пуст, вы можете использовать [[ -v PS1 ]] или же [ -v PS1 ] / test -v PS1 вместо. Обратите внимание, однако, что [[ ключевое слово и -v испытание [ а также test Встроенные оболочки, характерные для Bash. Не все другие раковины в стиле Борна принимают их. Поэтому вы не должны использовать их в таких сценариях, как ~/.profile а также /etc/profile это может выполняться в других оболочках (или с помощью диспетчера отображения, когда вы входите в систему графически), если только у вас нет чего-то еще в скрипте, который проверяет, какая оболочка работает, и выполняет команды, специфичные для Bash, только когда эта оболочка представляет собой Bash (например, проверка $BASH_VERSION).


Заметки

1 Эта статья подробно объясняет подоболочки. 3.2.4.3 Команды группировки справочного руководства Bash объясняют () синтаксис.

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

3 За исключением подоболочек. Возможно, это даже не исключение, поскольку подоболочки не "запускаются" в обычном смысле, который мы имеем в виду, когда говорим об этом. (На самом деле они не имеют значительного поведения при инициализации.) Обратите внимание, что при запуске bash - с аргументами или без них - внутри оболочки Bash, которая создает подпроцесс, который является оболочкой, но не является подоболочкой.

4 Обратите внимание, что не все оболочки - даже не все оболочки в стиле Борна - ведут себя таким образом. Но Bash это делает, и код Bash, включая код в сценариях запуска, очень часто полагается на него.

Другие вопросы по тегам