1. 程式人生 > 其它 >14.Python網路程式設計—Pexpect & paramiko

14.Python網路程式設計—Pexpect & paramiko

技術標籤:python網路程式設計python

14.1 Pexpect簡介

  • Pexpect 是 Don Libes 的 Expect 語言的一個 Python 實現,是一個用來啟動子程式,並使用正則表示式對程式輸出做出特定響應,以此實現與其 自動互動的 Python 模組。
  • Pexpect 的使用範圍很廣,可以用來實現與 ssh、ftp 、telnet 等程式的自動互動;可以用來自動複製軟體安裝包並在不同機器自動安裝;還可以 用來實現軟體測試中與命令列互動的自動化。

14.2 Pexpect 提供的 run() 函式

14.2.1 run() 的定義

  • 函式 run 可以用來執行命令,其作用與 Python os 模組中 system() 函式相似。 run() 是通過 Pexpect類實現的。
  • 如果命令的路徑沒有完全給出,則 run 會使用 which 命令嘗試搜尋命令的路徑 。

14.2.2 使用 run()執行 svn 命令

  • 與 os.system() 不同的是,使用 run() 可以方便地同時獲得命令的輸出結果與命令的退出狀態 。

14.2.3 run() 的返回值

  • command_out 中儲存的就是 /bin 目錄下的內容。

14.3 Pexpect 提供的 spawn() 類

14.3.1 使用 Pexpect 啟動子程式

  • spawn 的建構函式
  • spawn是Pexpect模組主要的類,用以實現啟動子程式,它有豐富的方法與子程式互動從而實現使用者對子程式的控制。
  • 它主要使用pty.fork() 生成子程序,並呼叫 exec() 系列函式執行 command 引數的內容。
  • 當子程式需要引數時,還可以使用一個引數的列表
  • 在建構函式中,maxread 屬性指定了 Pexpect 物件試圖從 tty 一次讀取的最大位元組數,它的預設值是2000位元組 。
  • 由於需要實現不斷匹配子程式輸出, searchwindowsize 指定了從輸入緩衝區中進行模式匹配的位置,預設從開始匹配。
  • logfile 引數指定了 Pexpect 產生的日誌的記錄位置。
  • 還可以將日誌指向標準輸出
  • 如果不需要記錄向子程式輸入的日誌,只記錄子程式的輸出,可以使用

14.3.2 使用 Pexpect 控制子程式

為了控制子程式,等待子程式產生特定輸出,做出特定的響應,可以使用expect 方法。

  • expect() 定義
    • 在引數中: pattern 可以是正則表示式, pexpect.EOF pexpect.TIMEOUT ,或者由這些元素組成的列表。
    • 需要注意的是,當pattern 的型別是一個列表時,且子程式輸出結果中不止一個被匹配成功,則匹配返回的結果是緩衝區中最先出現的那個元素,或者是列表中最左邊的元素。
    • 使用 timeout 可以指定等待結果的超時時間 ,該時間以秒為單位。
    • 當超過預訂時間時, expect 匹配到pexpect.TIMEOUT。
    • 如果難以估算程式執行的時間,可以使用迴圈使其多次等待直至等待執行結束
    • expect() 在執行中可能會丟擲兩種型別的異常分別是 EOF and TIMEOUF,其中 EOF 通常代表子程式的退出, TIMEOUT 代表在等待目標正則表示式中出現了超時。
    • 如果難以估算程式執行的時間,可以使用迴圈使其多次等待直至等待執行結束。
    • expect() 在執行中可能會丟擲兩種型別的異常分別是 EOF and TIMEOUF,其中 EOF 通常代表子程式的退出, TIMEOUT 代表在等待目標正則表示式中出現了超時。
    • expect 不斷從讀入緩衝區中匹配目標正則表示式,當匹配結束時 pexpect 的before 成員中儲存了緩衝區中匹配成功處之前的內容, pexpect 的 after 成員儲存的是緩衝區中與目標正則表示式相匹配的內容。
    • child.before 儲存的就是在根目錄下執行 ls 命令的結果。
  • send 系列函式
    • 這些方法用來向子程式傳送命令,模擬輸入命令的行為。
    • 與 send() 不同的是sendline() 會額外輸入一個回車符 ,更加適合用來模擬對子程式進行輸入命令的操作。
    • 當需要模擬傳送 “Ctrl+c” 的行為時,還可以使用 sendcontrol() 傳送控制字元。
  • 傳送 ctrl+c
    • 由於 send() 系列函式向子程式傳送的命令會在終端顯示,所以也會在子程式的輸入緩衝區中出現,因此不建議使用 expect 匹配最近一次 sendline() 中包含的字元。
    • 否則可能會在造成不希望的匹配結果。
  • interact() 定義
    • Pexpect還可以呼叫interact() 讓出控制權,使用者可以繼續當前的會話控制子程式。
    • 使用者可以敲入特定的退出字元跳出,其預設值為“^]” 。

14.4 Paramiko

  • paramiko是基於Python實現的SSH2遠端安全連線,支援認證及金鑰方式。可以實現遠端命令執行、檔案傳輸、中間SSH代理等功能,相對於Pexpect,封裝的層次更高,更貼近SSH協議的功能
  • paramiko包含兩個核心元件
    • 一個為SSHClient類
    • 另一個為SFTPClient類。

14.4.1 SSHClient類

  • SSHClient類是SSH服務會話的高階表示,該類封裝了傳輸(transport)、通道(channel)及SFTPClient的校驗、建立的方法,通常用於執行遠端命令。
  • connect方法
    • connect方法實現了遠端SSH連線並校驗
  • 引數說明:
    • hostname(str型別),連線的目標主機地址;
    • port(int型別),連線目標主機的埠,預設為22;
    • username(str型別),校驗的使用者名稱(預設為當前的本地使用者名稱);
    • password(str型別),密碼用於身份校驗或解鎖私鑰;
    • pkey(Pkey型別),私鑰方式用於身份驗證;
    • key_filename(str or list(str)型別),一個檔名或檔名列表,用於私鑰的身份驗證;
    • timeout(float型別),一個可選的超時時間(以秒為單位)的TCP連線;
    • allow_agent(bool型別),設定為False時用於禁用連線到SSH代理;
    • look_for_keys(bool型別),設定為False時用於來禁用在~/.ssh中搜索私鑰檔案;
    • compress(bool型別),設定為True時開啟壓縮。
  • exec_command方法
    • 遠端命令執行方法,該命令的輸入與輸出流為標準輸入(stdin)、輸出(stdout)、錯誤(stderr)的Python檔案對像。
  • 引數說明:
    • command(str型別),執行的命令串;
    • bufsize(int型別),檔案緩衝區大小,預設為-1(不限制)
  • load_system_host_keys方法
    • 載入本地公鑰校驗檔案,預設為~/.ssh/known_host,非預設路徑需要手工指定。
    • ~/.ssh/known_hosts的作用:當ssh會把你每個你訪問過計算機的公鑰(public key)都記錄在~/.ssh/known_hosts。
    • 當下次訪問相同計算機時,OpenSSH會核對公鑰。
    • 如果公鑰不同,OpenSSH會發出警告
  • 引數說明:
    • filename(str型別),指定遠端主機公鑰記錄檔案。
  • set_missing_host_policy方法
    • 設定連線的遠端主機沒有主機金鑰或HostKeys物件時的策略,目前支援三種,分別是AutoAddPolicy、RejectPolicy(預設)、WarningPolicy,僅限用於SSHClient類。
    • AutoAddPolicy,目標新增主機名及主機金鑰到本地HostKeys物件,並將其儲存,不依賴load_system_host_keys()的配置,即~/.ssh/hnown_hosts不存在也不產生影響;
    • RejectPolicy,自動拒絕未知的主機名和金鑰,依賴load_system_host_keys()的配置;
    • WarningPolicy,用於記錄一個未知的主機金鑰的Python警告,並接收它,功能上AutoAddPolicy相似,但未知主機會有告警。

14.4.2 SFTPClient類

  • SFTPClient作為一個SFTP客戶端物件,根據SSH傳輸協議的sftp會話,實現遠端檔案操作,比如檔案上傳、下載、許可權、狀態等操作。
  • from_transport方法
    • 建立一個已連通的SFTP客戶端通道。
  • 引數說明
    • t(transport),一個已通過驗證的傳輸物件。
  • put方法
    • 上傳本地檔案到遠端SFTP服務端
  • 引數說明:
    • localpath(str型別),需上傳的本地檔案(源);
    • remotepath(str型別),遠端路徑(目標);
    • callback(funcation(int,int)),獲取已接收的位元組數及總傳輸位元組數,以便回撥函式呼叫,預設為None;
    • confirm(bool型別),檔案上傳完畢後是否呼叫stat()方法,以便確認檔案的大小。
  • get方法
    • 從遠端SFTP服務端下載檔案到本地。
  • 引數說明:
    • remotepath(str型別),需要下載的遠端檔案(源);
    • callback(funcation(int,int)),獲取已接收的位元組數及總和傳輸位元組數,以便回撥函式呼叫,預設為None.
  • 其它方法
  • SFTPClient類其它常用方法說明:
    • mkdir,在SFTP服務端建立目錄,如sftp.mkdir("/home/userdir",mode=0777),預設模式是0777(八進位制),在某些系統上,mode被忽略。在使用它的地方,當前的umask值首先被遮蔽掉。
    • remove,刪除SFTP服務端指定目錄,如sftp.remove("/home/userdir")。
    • rename,重新命名SFTP服務端檔案或目錄,如sftp.rename("/home/test.sh","/home/testfile.sh")
    • stat,獲取遠端SFTP服務端指定檔案資訊,如sftp.stat("/home/testfile.sh")。
    • listdir,獲取遠端SFTP服務端指定目錄列表,以Python的列表(List)形式返回,如sftp.listdir("/home")。

14.4.3 實驗

In:

pip install paramiko

out:

Requirement already satisfied: paramiko in d:\programdata\anaconda3\lib\site-packages (2.7.1)
Requirement already satisfied: cryptography>=2.5 in d:\programdata\anaconda3\lib\site-packages (from paramiko) (2.7)
Requirement already satisfied: pynacl>=1.0.1 in d:\programdata\anaconda3\lib\site-packages (from paramiko) (1.4.0)
Requirement already satisfied: bcrypt>=3.1.3 in d:\programdata\anaconda3\lib\site-packages (from paramiko) (3.1.7)
Requirement already satisfied: cffi!=1.11.3,>=1.8 in d:\programdata\anaconda3\lib\site-packages (from cryptography>=2.5->paramiko) (1.12.3)
Requirement already satisfied: asn1crypto>=0.21.0 in d:\programdata\anaconda3\lib\site-packages (from cryptography>=2.5->paramiko) (1.0.1)
Requirement already satisfied: six>=1.4.1 in d:\programdata\anaconda3\lib\site-packages (from cryptography>=2.5->paramiko) (1.12.0)
Requirement already satisfied: pycparser in d:\programdata\anaconda3\lib\site-packages (from cffi!=1.11.3,>=1.8->cryptography>=2.5->paramiko) (2.19)
Note: you may need to restart the kernel to use updated packages.

In:

import paramiko

主機:xxx.xxx.xxx.xxx
使用者:student
密碼:xxxxxx
埠:xx
In:

sshc = paramiko.SSHClient()

In:

sshc.set_missing_host_key_policy(paramiko.AutoAddPolicy())

In:

sshc.connect(hostname='192.168.199.175',port=22,username='student',
            password='123456')

In:

stdin,stdout,stderr  = sshc.exec_command('df -m')

In:

print(stdout.read().decode('utf-8'))

out:

Filesystem     1M-blocks  Used Available Use% Mounted on
/dev/sda1          32354  6759     23946  23% /
tmpfs               1917     1      1917   1% /dev/shm