Чому 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команди. На сторінці man не згадується про це - лише згадка, яка scpвикористовується sshдля передачі даних. Це призводить до того, що якщо .bashrcмістяться команди, які друкують до стандартного виводу, передача файлів буде невдалою , наприклад, scp виходить з ладу без помилок .

Дивіться також цей пов'язаний звіт про помилки Red Hat від 15 років тому, scp перерви, коли в / etc / bashrc є команда echo (яка в кінцевому підсумку закрита як WONTFIX).

Чому scpі sftpне вдається

SCP (захищена копія) та SFTP (протокол захищеної передачі файлів) мають власні протоколи для локального та віддаленого кінців для обміну інформацією про передані файли (файли). Будь-який несподіваний текст із віддаленого кінця (помилково) інтерпретується як частина протоколу, і передача не вдається. Відповідно до поширених запитань із книги про равликів

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

Такий код повинен створювати вихід лише на інтерактивних логінах, якщо ttyдо входу додається стандартний вхід. Якщо він не зробить цей тест, він вставить ці текстові повідомлення там, де вони не належать: у цьому випадку, забруднюючи потік протоколу між scp2/ sftpі sftp-server.

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

  • Звичайні налаштування користувача (псевдоніми команд, змінні середовища, umask тощо) діють при запуску віддалених команд.
  • Поширена практика встановлення оболонки облікового запису на / bin / false для її відключення заважає власнику виконувати будь-які команди, якщо автентифікація все-таки випадково вдалася з якихось причин.

Деталі протоколу SCP

Для тих, хто цікавиться подробицями того, як працює SCP, я знайшов цікаву інформацію в розділі « Як працює протокол SCP», який містить деталі про Запуск scp з балакучими профілями оболонки на віддаленій стороні? :

Наприклад, це може статися, якщо ви додасте це до профілю оболонки на віддаленій системі:

відлуння ""

Чому він просто висить? Це відбувається з того, як scpу вихідному режимі чекає підтвердження першого протокольного повідомлення. Якщо це не двійковий номер 0, він очікує, що це повідомлення про віддалену проблему і чекає, поки більше символів сформує повідомлення про помилку, поки не надійде новий рядок. Оскільки ви не надрукували ще один новий рядок після першого, ваш локальний scpпросто залишається в циклі, заблокований read(2). Тим часом, після оброблення профілю оболонки на віддаленій стороні, scpв режимі мийки був запущений режим, який також блокується read(2), чекаючи бінарного нуля, що позначає початок передачі даних.

Висновок / TLDR

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


приємна стаття. але я повинен робити з цим? У мене є заїзд на .bashrc , але до сих пір ПКПП вихід з 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);

Чи джерело вашого .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?
тердон

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є більш безпечною, і все спростить.


Так, я це знаю. Ось чому неінтерактивні оболонки взагалі не створюють *bashrcфайли. Моє запитання, чому різні постачальники Linux стикаються з проблемою явного блокування неінтерактивної оболонки від читання цих файлів, якщо ці файли не читаються неінтерактивними оболонками.
тердон

1
@terdon: Оскільки оболонку можна назвати неінтерактивною оболонкою для входу , тоді оболонка входу буде джерелом .bash_profile, який може бути джерелом .bashrc.
cuonglm

Ні, в неінтерактивних оболонках єдине, що читається, - це $BASH_ENV. І те *profileй *bashrcінше ігнорується. Або, принаймні, так пише сторінка man. Однак, як показав Мікель, сторінка людини може брехати.
тердон

@terdon: Прочитайте мою оновлену відповідь із посиланням у ній. Ви намагалися bash -l -c :викликати неінтерактивну оболонку входу?
cuonglm

Ого. Ви абсолютно праві. Я читав цю частину близько --login100 разів, але, мабуть, вона ніколи не була зареєстрована. Дякую!
тердон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.