本文的情況,不同的linux系統版本,表現可能不同。
問題:默認情況下,paramiko在遠程主機上執行命令的時候,命令的搜索路徑為(/usr/local/bin:/bin:/usr/bin),這樣我們安裝的軟件,如果命令不在這些路徑下的話,就會執行錯誤,報找不到命令的錯誤
解決辦法:
- 就是在上面的路徑里(/usr/local/bin:/bin:/usr/bin)加我們需要命令的軟鏈接(ln /usr/install/jdk1.8.0_60/bin/java -s java)
- 把需要的路徑包含進去 stdin, stdout, stderr = ssh.exec_command('export PATH=$PATH:/usr/local/install/xxx/;echo $PATH')
- 先執行一條命令 stdin, stdout, stderr = ssh.exec_command('. ~/.bashrc;echo $PATH');stdin, stdout, stderr = ssh.exec_command('source ~/.bashrc;bash test.sh')
- 方法2和3,環境變量可以傳到后續執行的‘.sh’腳本里面去
- paramiko的ssh.exec_command()命令會開啟一個單獨的session,而且在exec_command中設定的環境變量不會傳遞給后續的腳本。解決方法是使用bash執行命令:
ssh.exec_command("bash -l -c 'some commands and some scripts...'")
bash -l -c解釋:-l(login)表示bash作為一個login shell;-c(command)表示執行后面字符串內的命令,這樣執行的腳本,可以獲取到/etc/profile里的全局變量,包括我們搜索命令的目錄PATH
-l
Make bash act as if it had been invoked as a login shell-c
If the -c option is present, then commands are read from string.- You're running the command passed to the
-c
argument.-l
makes it a login shell so bash first reads/etc/profile
,
遺留問題:為什么paramiko登錄后,只獲得路徑(/usr/local/bin:/bin:/usr/bin)
解答:密碼存在於被連接機器的/etc/ssh/sshd_config配置文件里;如下所示,sshd服務默認把(/usr/local/bin:/bin:/usr/bin)作為PATH的值
# $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/local/bin:/bin:/usr/bin # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options change a # default value.
對於上面的SSHD默認PATH值,不同的Op'e'nBOpenBSD不一樣
# $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $ 2 3 # This is the sshd server system-wide configuration file. See 4 # sshd_config(5) for more information. 5 6 # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin 7 8 # The strategy used for options in the default sshd_config shipped with 9 # OpenSSH is to specify options with their default value where 10 # possible, but leave them commented. Uncommented options override the
再說一個實際驗證的結論:
ssh -T admin@10.x.x.x 'echo $PATH' 獲得什么環境變量呢?
-T 表示不使用偽終端(man -a ssh----> -T Disable pseudo-terminal allocation.)
那么沒有偽終端的話,獲得什么樣的PATH呢(其他環境變量類似)?
首先,通過上面的知識可知,SSHD是有個默認PATH值的,編譯到SSHD(ssh_server)的代碼里的(SSHD可能是C語音寫的吧);
其次,不使用偽終端登錄后,首先執行的用戶目錄下的bashrc腳本(~/.bashrc),一般bashrc腳本里面會執行腳本/etc/profile或者/etc/bashrc之類的;總之只要被bashrc執行產生的全局變量(export)都會傳遞到后續的腳本或者命令行里
上一步需要注意的一點就是,sshd即ssh_server必須使用bash作為登錄shell,而不是zsh等其他的shell(我以為如果登錄shell是zsh,會執行~/.zshrc,結果發現我錯了),如果使用的zsh,發現只讀到寫死到sshd_server里面的PATH值
我本機測試的結果是,下面三種情況,都只能讀取sshd_server寫死的環境變量
stdin, stdout, stderr = ssh.exec_command('echo $PATH')和stdin, stdout, stderr = ssh.exec_command('echo $PATH',get_pty=True)和stdin, stdout, stderr = ssh.exec_command('echo $PATH',get_pty=False)
get_pty表示是否分配偽終端
paramiko中關於偽終端pty的定義和描述
@open_only def get_pty(self, term='vt100', width=80, height=24, width_pixels=0, height_pixels=0): """ Request a pseudo-terminal from the server. This is usually used right after creating a client channel, to ask the server to provide some basic terminal semantics for a shell invoked with `invoke_shell`. It isn't necessary (or desirable) to call this method if you're going to execute a single command with `exec_command`. :param str term: the terminal type to emulate (for example, ``'vt100'``) :param int width: width (in characters) of the terminal screen :param int height: height (in characters) of the terminal screen :param int width_pixels: width (in pixels) of the terminal screen :param int height_pixels: height (in pixels) of the terminal screen :raises: `.SSHException` -- if the request was rejected or the channel was closed """ m = Message() m.add_byte(cMSG_CHANNEL_REQUEST) m.add_int(self.remote_chanid) m.add_string('pty-req') m.add_boolean(True) m.add_string(term) m.add_int(width) m.add_int(height) m.add_int(width_pixels) m.add_int(height_pixels) m.add_string(bytes()) self._event_pending() self.transport._send_user_message(m) self._wait_for_event()
偽終端不是真實的終端,我們登錄unix界面,然后在里面啟動終端,這屬於真實的終端,假如我們直接通過ssh協議連接sshd(ssh admin@ip),這樣建立的就是偽終端。所以說偽終端不是真實的物理終端,但是卻具有真實終端的函數和功能。偽終端是由類似於xterm的終端模擬器創建的。
在unix系統中,大量的進程和I/O函數在控制終端中運行。偽終端可以處理控制字符(^C)
為什么使用pseudo-terminal就能終止遠程進程呢?
當SSH鏈接關閉,遠程服務器會關閉pty,同時發送SIGHUP
信號來終止遠程執行的命令。
SIGHUP is sent to the controlling process (session leader) associated with a controlling terminal if a disconnect is detected by the terminal interface.
參考:
1、http://feihu.me/blog/2014/env-problem-when-ssh-executing-command-on-remote/
2、https://gist.github.com/yegle/1564928