ssh執行遠端指令碼遇到的坑

weixin_33860722發表於2018-09-21

最新在搭建新的產品環境就遇到了一個坑,使用js 的node-ssh module執行遠端機器的sh指令碼的時候發現,無法獲取全域性的環境變數。我們知道linux機器的全域性環境變數的設定是在/etc/profile這個檔案中的。我在這個檔案中新增了JAVA_HOME的環境變數,並將java的bin目錄新增到了$PATH中,但是通過ssh執行遠端指令碼的時候拿到的JAVA_HOME的值是空的,但是如果通過xshell工具ssh到遠端主機就可以拿到環境變數,當時不理解是為什麼?後臺通過查了一些資料才瞭解大致原因。

原來通過ssh執行遠端的命令或者指令碼和通過ssh登入到遠端主機後在執行指令碼這兩種方式的bash模式不同。bash工作模式主要分為:login + interactive,login + non-interactive ,non-login + interactive ,non-login + non-interactive 。login 是指需要輸入使用者名稱和密碼登陸的 shell; interactive 顧名思義為互動式,命令的標準輸入與輸出為該 bash 繫結到的終端。

* login + interactive
  * 登陸 Linux 獲取的第一個 shell 
  * 通過 ssh user_name @ romote_ip 登陸獲取到的 shell 
  * 執行命令 bash -l 進入的 shell 
    * 首先讀取 /etc/profile 檔案 
    * 再從下面三個檔案讀取到第一個存在的檔案
      ~/.bash_profile、~/.bash_login、~/.profile 
      etc/profile 會依次讀取 /etc/profile.d 下所有檔案 
* login + non-interactive
  * 執行命令 bash -l script.sh 
    *配置檔案讀取同上 該模式比較少用
* non-login + interactive 
  * 執行命令bash
    *  讀取 /etc/bash.bashrc
    *  讀取 ~/.bashrc檔案 
* non-login + non-interactive
  * 執行命令 bash script.sh 
    * 讀取環境變數 $BASH_ENV 的值,匯入該值的配置檔案 
  • 通過SSH登入後在執行指令碼
    這種方式使用的是Bash的interactive + login shell模式。
    這種模式下回讀取優先讀取/etc/profile的配置,所謂我設定的環境變數是能夠讀取到的。

  • 通過ssh遠端執行指令碼命令
    這個方式使用的是bash的non-login + non-interactive模式。是不會讀取/etc/profile中的配置,所以我設定的環境變數是獲取不到的,於是我就去之前已經搭建好的機器上檢視了$BASH_ENV這個變數的值,發現是空的,這就很奇怪了,為什麼這個值是空的ssh遠端執行指令碼的時候也能成功的拿到JAVA_HOME這個環境變數呢? 後來發現是在~/.bashrc中檔案中設定的,但是按照上面的工作模式應該是不會讀取這個配置的,於是就又去查了一下資料,從幫助文件中找到了原因:

Bash attempts to determine when it is being run with its standard input connected to a network connection, 
as when executed by the remote shell daemon, 
usually rshd, or the secure shell daemon sshd.
If bash determines it is being run in this fashion,
it reads and executes commands from ~/.bashrc, 
if that file exists and is read‐ able.

大致的意思就是 bash 會判斷標準輸入是否關聯到 sshd 這樣的網路連結上,如果是,會讀取 ~/.bashrc 配置檔案。

所以要解決SSH遠端執行命令時找不到自定義環境變數的問題,那麼可以在登入使用者的HOME目錄的.bashrc中新增需要的環境變數。

相關文章