我本地和服務器的連接一直使用的是 Xshell 5,而在與服務器進行文件操作的時候使用的是 Xshell 推薦安裝的一個工具 Xftp 5,然而,昨天自己想着從服務器下載備份好的的數據庫文件到本地的時候發現這個文件傳輸工具居然過期不能用了,好氣啊!於是沒辦法(機智如我)只好用 Python 來實現 SSH 的連接,順便從服務器批量下載一些文件,實現自動化。

項目介紹

SSH 使用的庫

首先需要介紹一個 Python 實現 SSH 連接的第三方庫,名字叫做 paramiko,經過一個短暫的熟悉,我發現這個庫基本可以實現 SSH 連接中的一些常用方法,具體使用可以去看一些教程或者官方文檔。

我還是比較喜歡從實際的應用出發來加深對一些新接觸的第三方庫的認知,所以有了這篇文章中涉及到的實際應用案例。

腳本思路

首先來介紹一下我這個簡單的自動化腳本做的事情(由於想實現的事情比較單一且固定,所以直接寫成了幾個函數,寫的比較隨意):

  1. 首先創建一個配置文件,用來存放登錄服務器的一些參數,例如服務器 host,端口 port,用戶名稱和密碼等。
  2. 讀取配置文件的信息,返回一個字典以備后續調用
  3. 使用 SSH 鏈接服務器,並且執行幾個 shell 命令,返回需要下載的文件的絕對地址列表
  4. 連接 SFTP 批量下載文件到本地

源碼解讀

源碼展示

# -*- coding: utf-8 -*-
import paramiko import os from configparser import ConfigParser # 讀取配置文件獲取服務器的登錄信息 def read_ini(): info = dict() cf = ConfigParser() cf.read('config.ini', encoding='utf-8') keys = cf.options('ssh') for each in keys: info[each] = cf.get('ssh', each) print(info) return info # 連接服務區並執行shell命令返回輸出結果 def ssh_test(host, port, username, password): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 連接服務器 try: ssh.connect(hostname=host, port=port, username=username, password=password) except Exception as e: print(e) return # 設置一個內部函數,執行shell命令並返回輸出結果 def run_shell(cmd): ssh_in, ssh_out, ssh_error = ssh.exec_command(cmd) result = ssh_out.read() or ssh_error.read() return result.decode().strip() # 獲取指定文件夾的絕對地址 cmd_get_path = 'cd dbs;pwd' db_path = run_shell(cmd_get_path) # 獲取指定文件夾中文件的名稱,並跟上面得到的文件夾絕對地址組合起來 cmd_get_sqls = 'cd dbs;ls' sqls = run_shell(cmd_get_sqls) lis = ['{}/{}'.format(db_path, each) for each in sqls.split('\n')] print(lis) # 關閉連接 ssh.close() return lis # 鏈接服務器進行文件傳輸 def sftp_test(host, port, username, password, from_file, to_file): transport = paramiko.Transport((host, port)) try: transport.connect(username=username, password=password) except Exception as e: print(e) return sftp = paramiko.SFTPClient.from_transport(transport) # 將文件下載到本地,如果是上傳使用 put sftp.get(from_file, to_file) transport.close() if __name__ == '__main__': info = read_ini() h = info.get('host', None) p = int(info.get('port', None)) # 端口是int類型 u = info.get('username', None) pw = info.get('password', None) files = ssh_test(h, p, u, pw) path = 'F:\\dbs' if files: for each in files: name = each.split('/')[-1] ss = sftp_test(h, p, u, pw, each, os.path.join(path, name)) 

配置文件讀取

首先,配置文件是放在跟腳本同目錄下的,文件名稱為 config.ini,配置的信息格式遵循一般的配置文件的格式,如下:

[ssh]
host=119.23.106.34 port=22 username=user password=password 

只需要提供以上四個信息就可以連接到服務器。

讀取配置信息的方式是函數 read_ini(),這個函數使用 Python 內置的庫 configparser 去讀取配置文件,並且返回一個鍵值對的字典,以供后續的函數調用。

def read_ini(): info = dict() cf = ConfigParser() cf.read('config.ini', encoding='utf-8') keys = cf.options('ssh') for each in keys: info[each] = cf.get('ssh', each) print(info) return info 

SSH 連接執行

ssh_test 函數是用來連接 SSH 的方法,這個方法接受4個參數,也就是上面的配置文件需要提供的參數。

首先需要創建一個 ssh 連接的實例:

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

然后傳入相關參數,嘗試連接到遠程服務器:

try: ssh.connect(hostname=host, port=port, username=username, password=password) except Exception as e: print(e) return 

當服務器已經連接成功之后,可以進行 shell 命令的操作了,我把這個執行 shell 命令的操作過程寫到一個內嵌的函數中,這樣可以方便執行不同的命令:

def run_shell(cmd): ssh_in, ssh_out, ssh_error = ssh.exec_command(cmd) result = ssh_out.read() or ssh_error.read() return result.decode().strip() 

ssh.exec_command(cmd) 會得到三個對象,其中第二個就是命令執行成功的結果,第三個是命令執行失敗的結果,所以我們可以取第二個的結果作為命令執行成功返回的結果,結果需要轉碼,並且要去掉末尾的換行符。

這里我首先執行了一條 shell 命令,多個命令直接需要使用分號隔開,這個命令是返回當前文件夾的絕對地址:

cmd_get_path = 'cd dbs;pwd'

命令執行的結果放到一個變量中保存,后續需要調用:

db_path = run_shell(cmd_get_path)

然后第二條 shell 命令是返回指定文件夾下的所有文件,我這里是返回的自己的服務器上面數據庫備份的文件,通過看代碼就能看到我這里處理了一下文件名稱,讓它們變成了絕對地址,這樣方便后續下載文件。

最后這個函數返回的就是一個服務器上面的文件夾中包含的所有文件的絕對地址組成的列表。

SFTP 下載文件

下載文件的操作寫在函數 sftp_test() 中,這個函數除了要傳遞登錄服務器的4個基本參數外,還要傳遞2個參數,第一個是服務器上面的文件的絕對地址,第二個是本地保存的文件的地址(相對地址和絕對地址都行)。

看代碼,這里和連接 SSH 有一些區別,不過大體的思路一樣,都是先創建實例,然后嘗試連接:

transport = paramiko.Transport((host, port)) try: transport.connect(username=username, password=password) except Exception as e: print(e) return sftp = paramiko.SFTPClient.from_transport(transport) 

連接之后,就可以使用 get() 方法來下載文件了,如果要上傳的話,可以使用與之對應的 put() 方法:

sftp.get(from_file, to_file) 

執行代碼

最后執行代碼的過程其實就是之前講到的項目思路,首先運行配置文件讀取的函數,讀取配置:

info = read_ini() h = info.get('host', None) p = int(info.get('port', None)) # 端口是int類型 u = info.get('username', None) pw = info.get('password', None) 

這里需要注意,由於端口接受的是一個 int 類型,而在配置中是字符串,所有需要轉換一下才能使用,不然就會報錯。

讀取了配置就可以連接 SSH 然后返回文件的絕對地址:

files = ssh_test(h, p, u, pw) 

最后使用循環來分別下載每個文件到本地保存即可:

path = 'F:\\dbs' if files: for each in files: name = each.split('/')[-1] ss = sftp_test(h, p, u, pw, each, os.path.join(path, name)) 

總結:使用 Python 連接服務器進行操作在運維自動化中應該使用會比較多,這篇文章主要是通過一個實例來介紹一下 Python 連接 SSH 之后的基本操作,還有更多的操作有待讀者自己去學習和實戰。

 原創文章,轉載請注明出處:http://www.tendcode.com/article/python-ssh/