通過SSH執行遠程主機的命令或腳本時,經常會出現找不到自定義環境變量的問題。但是,如果通過SSH登錄遠程主機,然后再執行相同的命令或腳本,那么此時執行又是成功的。兩種相似的方法,得到的結果卻截然不同,看起來很詭異的現象,根本原因在於這兩種方式使用的bash模式不同!
1. 通過SSH登錄后再執行命令和腳本
這種方式會使用Bash的interactive + login shell模式,這里面有兩個概念需要解釋:interactive和login。
login故名思義,即登陸,login shell是指用戶以非圖形化界面或者以ssh登陸到機器上時獲得的第一個shell,簡單些說就是需要輸入用戶名和密碼的shell。因此通常不管以何種方式登陸機器后用戶獲得的第一個shell就是login shell。
interactive意為交互式,這也很好理解,interactive shell會有一個輸入提示符,並且它的標准輸入、輸出和錯誤輸出都會顯示在控制台上。所以一般來說只要是需要用戶交互的,即一個命令一個命令的輸入的shell都是interactive shell。而如果無需用戶交互,它便是non-interactive shell。通常來說如bash script.sh
此類執行腳本的命令就會啟動一個non-interactive shell,它不需要與用戶進行交互,執行完后它便會退出創建的Shell。
在interactive + login shell模式中,Shell首先會加載/etc/profile
文件,然后再嘗試依次去加載下列三個配置文件之一,一旦找到其中一個便不再接着尋找:
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
2. 通過SSH直接執行遠程命令和腳本
這種方式會使用Bash的non-interactive + non-login shell模式,它會創建一個shell,執行完腳本之后便退出,不再需要與用戶交互。
no-login shell,顧名思義就是不是在登錄Linux系統時啟動的(比如你在命令行提示符上輸入bash啟動)。它不會去執行/etc/profile
文件,而會去用戶的HOME目錄檢查.bashrc
並加載。
系統執行Shell腳本的時候,就是屬於這種non-interactive shell。Bash通過BASH_ENV
環境變量來記錄要加載的文件,默認情況下這個環境變量並沒有設置。如果有指定文件,那么Shell會先去加載這個文件里面的內容,然后再開始執行Shell腳本。
3. 結論
由此可見,如果要解決SSH遠程執行命令時找不到自定義環境變量的問題,那么可以在登錄用戶的HOME目錄的.bashrc
中添加需要的環境變量。
4、示例
當登錄之后,直接在某台遠程主機:10.0.63.9上執行日期格式化的命令時,打印的是正確的,如下:
[root@dev-appserver2 ~]# buildTimeStamp=2017-09-27T16:58:47.291+08:00; buildTimeStampStr=$(date -d $buildTimeStamp "+%y%m%d%H%M%S"); echo $buildTimeStampStr 170927165847
當在另外一台主機(10.0.251.216)上遠程執行ssh命令時,打印的結果不正確(差了八個時區),如下:
[root@host-10-0-251-216 ~]# cat sshtime buildTimeStamp=2017-09-27T16:58:47.291+08:00; buildTimeStampStr=$(date -d $buildTimeStamp "+%y%m%d%H%M%S"); echo $buildTimeStampStr [root@host-10-0-251-216 ~]# a=`cat sshtime ` [root@host-10-0-251-216 ~]# echo $a buildTimeStamp=2017-09-27T16:58:47.291+08:00; buildTimeStampStr=$(date -d $buildTimeStamp "+%y%m%d%H%M%S"); echo $buildTimeStampStr [root@host-10-0-251-216 ~]# ssh root@10.0.63.9 $a root@10.0.63.9's password: 170927085847
此時修改10.0.63.9上,root根目錄下的.bashrc文件,增加TZ的設置,再次執行ssh打印的結果是正確的:
[root@dev-appserver2 ~]# cat .bashrc # .bashrc # User specific aliases and functions alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' export TZ="Asia/Shanghai" # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi [root@dev-appserver2 ~]#
[root@host-10-0-251-216 ~]# ssh root@10.0.63.9 $a root@10.0.63.9's password: 170927165847