Bash

為什麼 bashrc 會檢查目前 shell 是否是互動式的?

  • September 29, 2021

在我的 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,非互動式 shell 甚至不讀取這些文件:

bash以非互動方式啟動時,例如執行一個 shell 腳本,它會BASH_ENV在環境中查找變數,如果它出現在那裡,則擴展它的值,並將擴展的值用作要讀取和執行的文件的名稱。Bash 的行為就像執行了以下命令:if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fiPATH變數的值不用於搜尋文件名。

如果我理解正確,只有設置為指向它們*.bashrc時才會讀取文件。BASH_ENV這是不可能偶然發生的事情,只有當有人相應地明確設置變數時才會發生。

這似乎打破了.bashrc通過設置讓腳本自動獲取使用者的可能性BASH_ENV,這可能會派上用場。鑑於 bash 在非互動式執行時永遠不會讀取這些文件,除非明確告知這樣做,為什麼預設*bashrc文件不允許這樣做?

這是我幾週前打算在這裡發布的一個問題。像terdon一樣,我知道 a.bashrc僅用於互動式 Bash shell,因此不需要.bashrc檢查它是否在互動式 shell 中執行。令人困惑的是,我使用的所有發行版(Ubuntu、RHEL 和 Cygwin)都有某種類型的檢查(測試$-$PS1)以確保目前的 shell 是互動式的。我不喜歡貨物崇拜程式,所以我開始在我的.bashrc.

Bash 有一個用於遠端 shell 的特殊情況

在研究了這個問題後,我發現遠端 shell的處理方式不同。雖然非互動式 Bash shell 通常不會~/.bashrc在啟動時執行命令,但當 shell 被遠端 shell 守護程序呼叫時,會出現一種特殊情況:

Bash 嘗試確定它何時在其標準輸入連接到網路連接的情況下執行,例如由遠端 shell 守護程序(通常rshd)或安全 shell 守護程序執行時sshd。如果 Bash 確定它正在以這種方式執行,它會從 ~/.bashrc 讀取並執行命令,前提是該文件存在並且可讀。如果呼叫為 ,它將不會執行此操作sh。該--norc選項可用於禁止此行為,該--rcfile選項可用於強制讀取另一個文件,但既不rshd也不sshd通常使用這些選項呼叫 shell 或允許指定它們。

例子

在遙控器的開頭插入以下內容.bashrc。(如果.bashrc來自.profileor .bash_profile,則在測試時暫時禁用此功能):

echo bashrc
fun()
{
   echo functions work
}

在本地執行以下命令:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • No iin$-表示 shell是非互動式的
  • 沒有前導-表示$0外殼不是登錄外殼

遠端定義的 Shell 函式.bashrc也可以執行:

$ ssh remote_host fun
bashrc
functions work

我注意到~/.bashrc只有將命令指定為ssh. 這是有道理的:whenssh用於啟動正常登錄 shell,.profile.bash_profile執行(並且.bashrc僅在這些文件之一明確這樣做時才獲取源)。

.bashrc在執行(非互動式)遠端命令時,我可以看到 source 的主要好處是可以執行 shell 函式。但是,典型.bashrc中的大多數命令僅與互動式 shell 相關,例如,除非 shell 是互動式的,否則不會擴展別名。

遠端文件傳輸可能會失敗

rsh當或ssh用於啟動互動式登錄 shell 或使用非互動式 shell 執行命令時,這通常不是問題。但是,對於使用遠端 shell傳輸數據的程序來說,這可能是一個問題。rcp``scp``sftp

事實證明,遠端使用者的預設 shell(如 Bash)在使用該scp命令時是隱式啟動的。手冊頁中沒有提及這一點 - 僅提及scp用於ssh其數據傳輸。這樣做的結果是,如果.bashrc包含任何列印到標準輸出的命令,文件傳輸將失敗,例如 scp 失敗而沒有錯誤

另請參閱 15 年前相關的 Red Hat 錯誤報告,當 /etc/bashrc 中有 echo 命令時 scp 中斷(最終關閉為WONTFIX)。

為什麼scpsftp失敗

SCP(安全副本)SFTP(安全文件傳輸協議)具有自己的協議,用於本地和遠端端交換有關正在傳輸的文件的資訊。來自遠端端的任何意外文本(錯誤地)被解釋為協議的一部分,並且傳輸失敗。根據蝸牛書的常見問題解答

但是,經常發生的情況是,伺服器上的系統或每個使用者的 shell 啟動文件(.bashrc.profile/etc/csh.cshrc.login等)中有一些語句在登錄時輸出文本消息,供人類閱讀(如fortuneecho "Hi there!"、 ETC。)。

tty當附加到標準輸入時,此類程式碼應僅在互動式登錄時產生輸出 。如果它沒有進行這個測試,它將在它們不屬於的地方插入這些文本消息:在這種情況下,污染scp2/sftp和之間的協議流sftp-server

shell 啟動文件之所以重要,是因為它**sshd 在代表使用者啟動任何程序時使用了使用者的 shell** (例如使用 /bin/sh -c “command”)。這是 Unix 的傳統,具有以下優點:

  • 執行遠端命令時,使用者的正常設置(命令別名、環境變數、umask 等)生效。
  • 如果由於某種原因身份驗證仍然意外成功,將帳戶的 shell 設置為 /bin/false 以禁用它的常見做法將阻止所有者執行任何命令。

SCP協議細節

對於那些對 SCP 如何工作的細節感興趣的人,我在 SCP 協議的工作原理中找到了有趣的資訊,其中包括有關在遠端端使用健談的 shell 配置文件執行 scp 的詳細資訊?

例如,如果您將其添加到遠端系統上的 shell 配置文件中,就會發生這種情況:

迴聲“”

為什麼它只是掛起?這來自scp模式中如何等待第一個協議消息確認的方式。如果它不是二進制 0,它期望它是遠端問題的通知,並等待更多字元形成錯誤消息,直到新行到達。由於您沒有在第一行之後列印另一個新行,因此您的本地只是停留在一個循環中,被. 同時,在遠端端處理完 shell 配置文件後,在 sink 模式下啟動,這也阻塞 on ,等待表示數據傳輸開始的二進制零。scp``read(2)``scp``read(2)

結論 / TLDR

典型.bashrc中的大多數語句僅對互動式 shell 有用——在使用rshor執行遠端命令時無效ssh。在大多數此類情況下,不需要設置 shell 變數、別名和定義函式——如果使用scpsftp. 在驗證目前 shell 是非互動式後退出是.bashrc.

引用自:https://unix.stackexchange.com/questions/257571