Почему bashrc проверяет, является ли текущая оболочка интерактивной?

62

На моем Arch установить /etc/bash.bashrcи /etc/skel/.bashrcсодержать эти строки:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

На Debian /etc/bash.bashrcесть:

# 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

Однако согласно man bashнеинтерактивным оболочкам эти файлы даже не читаются:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

Если я правильно понимаю, *.bashrcфайлы будут прочитаны только в том случае, если на BASH_ENVних указано. Это то, что не может произойти случайно и произойдет только в том случае, если кто-то явно установил переменную соответствующим образом.

Похоже, это лишает возможности наличия сценариев, .bashrcавтоматически определяющих пользователя при настройке BASH_ENV, что может пригодиться. Учитывая, что bash никогда не будет читать эти файлы при неинтерактивном запуске, если явно не указано иное, почему *bashrcфайлы по умолчанию запрещают это?

Тердон
источник
5
Для практического ответа, см. SCP терпит неудачу без ошибок
Жиль "ТАК - перестань быть злым"

Ответы:

69

Это вопрос, который я собирался опубликовать здесь несколько недель назад. Как и тердон , я понял, что a создается .bashrcтолько для интерактивных оболочек Bash, поэтому не нужно .bashrcпроверять, работает ли он в интерактивной оболочке. Смущает то, что все дистрибутивы, которые я использую (Ubuntu, RHEL и Cygwin), имели некоторую проверку (тестирование $-или $PS1), чтобы убедиться, что текущая оболочка является интерактивной. Мне не нравится программирование грузового культа, поэтому я решил понять назначение этого кода в своем .bashrc.

Bash имеет специальный чехол для удаленных оболочек

Изучив проблему, я обнаружил, что удаленные оболочки обрабатываются по-разному. Хотя неинтерактивные оболочки Bash обычно не запускают ~/.bashrcкоманды при запуске, особый случай возникает, когда оболочка вызывается демоном удаленной оболочки :

Bash пытается определить, когда он запускается со своим стандартным входом, подключенным к сетевому соединению, как при выполнении, как правило rshd, демоном удаленной оболочки или демоном защищенной оболочки sshd. Если Bash определяет, что он выполняется таким образом, он читает и выполняет команды из ~ / .bashrc, если этот файл существует и доступен для чтения. Он не будет делать это, если вызывается как sh. Параметр --norcможет использоваться для запрета этого поведения, а --rcfileпараметр может использоваться для принудительного чтения другого файла, но ни при этом, rshdни в sshdобщем случае не вызывает оболочку с этими параметрами и не позволяет их указывать.

пример

Вставьте следующее в начале пульта .bashrc. (Если .bashrcиспользуется источник .profileили .bash_profile, временно отключите это во время тестирования):

echo bashrc
fun()
{
    echo functions work
}

Выполните следующие команды локально:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Нет iв $-означает, что оболочка не является интерактивной .
  • Не приводя -в $0указывает , что оболочка не Войти оболочка .

Функции оболочки, определенные в пульте дистанционного управления, .bashrcтакже могут быть запущены:

$ ssh remote_host fun
bashrc
functions work

Я заметил , что ~/.bashrcэто только получен , когда команда задается в качестве аргумента для ssh. Это имеет смысл: когда sshиспользуется для запуска обычной оболочки входа .profileили .bash_profileзапуска (и .bashrcтолько из источника, если это явно сделано одним из этих файлов).

Основное преимущество, которое я вижу в .bashrcиспользовании удаленной команды (неинтерактивной), заключается в том, что можно запускать функции оболочки. Тем не менее, большинство команд в типичном случае .bashrcотносятся только к интерактивной оболочке, например, псевдонимы не раскрываются, если оболочка не является интерактивной.

Удаленная передача файлов может быть неудачной

Обычно это не проблема, когда rshили sshиспользуется для запуска интерактивной оболочки входа в систему или когда неинтерактивные оболочки используются для запуска команд. Тем не менее, это может быть проблемой для таких программ, как rcp, scpи sftpкоторые используют дистанционные оболочки для передачи данных.

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

Смотрите также этот связанный отчет об ошибках Red Hat 15 лет назад, scp прерывается, когда в / etc / bashrc есть команда echo (которая в итоге была закрыта как WONTFIX).

Почему scpи sftpне удалось

SCP (защищенное копирование) и SFTP (защищенный протокол передачи файлов) имеют свои собственные протоколы для локального и удаленного конца для обмена информацией о передаваемых файлах. Любой неожиданный текст с удаленного конца (ошибочно) интерпретируется как часть протокола, и передача завершается неудачно. Согласно FAQ из Книги Улитки

Часто случается, однако, является то , что есть заявления в любой системе или для каждого пользователя файлов запуск оболочки на сервере ( .bashrc, .profile, /etc/csh.cshrc, .loginи т.д.) , которые вывод текстовые сообщения на входе, предназначенные для чтения человека (например fortune, echo "Hi there!", и т.д.).

Такой код должен выводить только при интерактивных входах в систему, когда есть ttyпривязка к стандартному вводу. Если он не выполнит этот тест, он вставит эти текстовые сообщения там, где они не принадлежат: в этом случае, загрязняя поток протокола между scp2/ sftpи sftp-server.

Причина, по которой файлы запуска оболочки актуальны, заключается в том, что sshd при запуске любых программ от имени пользователя используется оболочка пользователя (например, с помощью / bin / sh -c "command"). Это традиция Unix и имеет преимущества:

  • Обычные настройки пользователя (псевдонимы команд, переменные среды, umask и т. Д.) Действуют при выполнении удаленных команд.
  • Обычная практика установки оболочки учетной записи на / bin / false, чтобы отключить ее, не позволит владельцу запускать какие-либо команды, если по какой-либо причине аутентификация все же случайно пройдет успешно.

Детали протокола SCP

Для тех, кто интересуется подробностями того, как работает SCP, я нашел интересную информацию в разделе Как работает протокол SCP, который включает подробности о запуске scp с разговорчивыми профилями оболочки на удаленной стороне? :

Например, это может произойти, если вы добавите это в свой профиль оболочки в удаленной системе:

эхо ""

Почему просто висит? Это происходит из-за того, как scpв режиме источника ожидается подтверждение первого сообщения протокола. Если это не двоичный 0, он ожидает, что это уведомление об удаленной проблеме, и ждет, пока больше символов сформирует сообщение об ошибке, пока не прибудет новая строка. Так как вы не печатали еще одну новую строку после первой, ваша локальная система scpостается в цикле и заблокирована read(2). Между тем, после обработки профиля оболочки на удаленной стороне scpбыл запущен режим приемника, который также блокируется read(2), ожидая двоичного нуля, обозначающего начало передачи данных.

Заключение / TLDR

Большинство операторов в типичном .bashrcслучае полезны только для интерактивной оболочки, но не при выполнении удаленных команд с помощью rshили ssh. В большинстве таких ситуаций установка переменных оболочки, псевдонимов и определение функций нежелательна, а печать любого текста в стандартном формате активно вредна при передаче файлов с использованием таких программ, как scpили sftp. Выход из режима после проверки того, что текущая оболочка не является интерактивной, является наиболее безопасным для поведения .bashrc.

Энтони Дж - справедливость для Моники
источник
хорошая статья но я должен сделать с этим? У меня есть регистрация по .bashrc, но все еще scp выход с кодом 0 и я ничего не
ses
@ses Было бы лучше задать новый вопрос, где вы можете описать вашу ситуацию более подробно.
Энтони Дж. - правосудие для Моники
16

Страница man не учитывает, что bashтакже есть источники .bashrcдля неинтерактивных удаленных оболочек, как в

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);
Mikel
источник
Вы берете свой .bashrc из вашего .bash_profile (или .profile)?
Гленн Джекман
Да, но это не главное. Пустой .bash_profileбудет демонстрировать такое же поведение.
Микель
Не уверен, что это показывает мне. Вы говорите, что rsh(которые я никогда не использовал) источники ~/.bashrc? И что, по сути, дистрибутивы Linux блокируют его на bashслучай, если кто-нибудь когда-нибудь попытается запустить скрипт rsh? ssh serverне должен касаться, .bashrcтак как это оболочка входа в систему в любом случае. Только если ваша система не относится к тем источникам, из которых ~ / .bashrc` ~/.profile. И ssh server commandдаже не должен этого делать. Конечно, не в моей системе.
Тердон
2
Я связал вас с bashисточником, а не с rshисточником. :) Комментарий относится и к тому ssh, что он был написан очень давно. Смотрите обновленный ответ с большим количеством источников.
Микель
Ах, это полезно, спасибо. Как бы я это проверил? Я попытался добавить echoв самый верх /etc/bash.bashrcи ~/.bashrc(до интерактивных тестов оболочки), а затем ssh в целевую машину с ssh server hostnameи во время hostnameработы, я не увидел ни одного из моих эхо-сигналов. Разве это не мое shell_level(что бы это ни было) <2?
Terdon
5

По соглашению, .bashrcэто место, где пользователь хранит конфигурацию настройки для оболочки.

Этими настройками конфигурации могут быть переменные среды, псевдонимы, необычные приглашения. С неинтерактивной оболочкой эти недостатки бессмысленны. Более того, неинтерактивная оболочка может вызываться во многих контекстах, вы не уверены, что эти переменные среды могут привести к ложноотрицательному случаю или даже уязвимы для безопасности.

Ближайший пример - псевдоним:

alias cp='cp -i'

Тогда он навсегда повесит вашу неинтерактивную оболочку.

Таким образом, проверка выполняется наверху, .bashrcчтобы гарантировать, что у нас не будет проблем.


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

Когда оболочка называется не-интерактивная оболочка , это источник /etc/profile, то источник первым нашел в ~/.bash_profile, ~/.bash_loginи ~/.profile:

Когда bash вызывается как интерактивная оболочка входа в систему или как неинтерактивная оболочка с параметром --login , она сначала читает и выполняет команды из файла / etc / profile, если этот файл существует. После прочтения этого файла он ищет ~ / .bash_profile, ~ / .bash_login и ~ / .profile в указанном порядке, а также читает и выполняет команды из первой, которая существует и доступна для чтения.

Ничто не препятствует получению этих файлов .bashrcсамим, поэтому проверка внутри .bashrcболее безопасна и упрощает задачу.

cuonglm
источник
Да, я знаю это. Вот почему неинтерактивные оболочки вообще не содержат исходных *bashrcфайлов. Мой вопрос заключается в том, почему различные поставщики Linux сталкиваются с проблемой явной блокировки неинтерактивных оболочек от чтения этих файлов, если эти файлы в любом случае не читаются неинтерактивными оболочками.
Terdon
1
@terdon: Поскольку оболочка может называться неинтерактивной оболочкой входа в систему , то оболочкой входа будет источник .bash_profile, который может быть источником .bashrc.
cuonglm
Нет, в неинтерактивных оболочках читается только одно $BASH_ENV. Оба *profileи *bashrcигнорируются. Или, по крайней мере, так говорится на странице руководства. Однако, как показал Микель, страница справочника может быть лживой.
Terdon
@terdon: Прочитайте мой обновленный ответ со ссылкой в ​​нем. Вы пытались bash -l -c :вызвать неинтерактивную оболочку входа?
cuonglm
Ух ты. Вы совершенно правы. Я прочитал эту часть около --login100 раз, но, видимо, она никогда не регистрировалась. Спасибо!
Terdon