為什麼 bashrc 會檢查目前 shell 是否是互動式的?
在我的 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"; fi
但PATH
變數的值不用於搜尋文件名。如果我理解正確,只有設置為指向它們
*.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
來自.profile
or.bash_profile
,則在測試時暫時禁用此功能):echo bashrc fun() { echo functions work }
在本地執行以下命令:
$ ssh remote_host 'echo $- $0' bashrc hBc bash
- No
i
in$-
表示 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
)。為什麼
scp
和sftp
失敗SCP(安全副本)和SFTP(安全文件傳輸協議)具有自己的協議,用於本地和遠端端交換有關正在傳輸的文件的資訊。來自遠端端的任何意外文本(錯誤地)被解釋為協議的一部分,並且傳輸失敗。根據蝸牛書的常見問題解答
但是,經常發生的情況是,伺服器上的系統或每個使用者的 shell 啟動文件(
.bashrc
、.profile
、/etc/csh.cshrc
、.login
等)中有一些語句在登錄時輸出文本消息,供人類閱讀(如fortune
、echo "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 有用——在使用rsh
or執行遠端命令時無效ssh
。在大多數此類情況下,不需要設置 shell 變數、別名和定義函式——如果使用scp
或sftp
. 在驗證目前 shell 是非互動式後退出是.bashrc
.