HOWTO · Python
在 Python 中使用 SCP 協議
本教程討論了在 Python 中使用安全複製協議。
本頁內容
scp 指令與 Python 的 SCPClient 都能透過 SSH 在本機與遠端伺服器之間傳輸檔案。若要在 Python 程式中管理 SSH 連線及傳輸呼叫,請使用 SCPClient;若已安裝 OpenSSH 的 scp 程式,且希望由 Python 啟動它,則可使用 subprocess.run()。無論採用哪種方式,傳輸資料前都應先設定驗證方式與受信任的主機金鑰。
在 Python 中使用 SCP 模組傳輸檔案
請在執行應用程式的環境中安裝 paramiko 和 scp 套件。SSHClient 會開啟 SSH 連線,而 SCPClient 則透過該連線的傳輸通道傳送或接收檔案。這個程式庫會透過 SSH 使用其 SCP 實作;範例中偶爾會重複提到較舊的 SCP1/SSH1 說法,但不應將其當作目前 Paramiko 與 scp 套件行為的一般描述。
以下指令碼會從環境變數讀取主機、使用者和本機檔案路徑。請將預設值換成自己的連線資訊;若要使用其他傳輸方式,請將 SCP_MODE 設為 download、recursive 或 memory。預設的 upload 模式會將本機檔案傳送至 /home/<user>/。範例會載入已知主機金鑰,並保留 Paramiko 預設的未知主機金鑰處理政策,因此不會默默信任未知主機。若有可用的 SSH agent 或已設定的金鑰檔,範例會使用它們;請依照自己的環境設定適用的驗證選項。此網路範例沒有附上實際觀察到的輸出。
import io
import os
def transfer():
host = os.environ.get("SCP_HOST")
if not host:
return
import paramiko
from scp import SCPClient
user = os.environ.get("SCP_USER", "your_user")
local_path = os.environ.get("SCP_LOCAL_FILE", "report.txt")
remote_path = f"/home/{user}/report.txt"
mode = os.environ.get("SCP_MODE", "upload")
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
scp = None
try:
ssh.connect(hostname=host, username=user)
scp = SCPClient(ssh.get_transport())
if mode == "download":
scp.get(remote_path, local_path)
elif mode == "recursive":
scp.put("local_directory", remote_path=f"/home/{user}/", recursive=True)
elif mode == "memory":
scp.putfo(io.BytesIO(b"report contents\n"), remote_path)
else:
scp.put(local_path, remote_path)
finally:
try:
if scp is not None:
scp.close()
finally:
ssh.close()
if __name__ == "__main__":
transfer()
put(local_path, remote_path) 會上傳本機檔案,get(remote_path, local_path) 則會下載檔案。put(..., recursive=True) 可傳送整個目錄樹。putfo() 接受二進位類檔案物件,適合用於內容已載入記憶體的情況;其第一個引數必須能從目前位置讀取。即使傳輸時發生錯誤,finally 區塊仍會先關閉 SCP 用戶端,再關閉 SSH 用戶端。請保持主機金鑰檢查啟用,並設定所需的金鑰、agent 或其他受支援的驗證方式,不要將密碼直接寫在原始碼中。載入系統主機金鑰只會提供受信任的金鑰;它不會提供驗證憑證,也不保證所有伺服器都已列入已知主機。
使用 subprocess.run() 函式透過 SCP 傳輸檔案
subprocess.run() 會啟動本機程序。它不會在遠端伺服器上執行 shell 指令:實際處理傳輸並建立 SSH 連線的是本機的 scp 可執行程式。本機必須已安裝 scp,而且 SSH 設定、金鑰或 agent,以及 known-hosts 檔案都必須允許建立連線。請使用引數清單並維持 shell=False(預設值),讓路徑和選項分別作為引數傳入。
設定下方的環境變數,即可將本機檔案傳送至遠端目的地。若要下載,請反轉來源與目的地引數,讓遠端路徑排在前面。指令碼會捕捉本機找不到可執行程式的情況,並透過回傳值回報非零結束狀態;若未設定伺服器或驗證無法運作,它不會宣稱傳輸成功。這個範例同樣未曾在遠端主機上執行。
import os
import subprocess
def copy_file():
host = os.environ.get("SCP_HOST")
if not host:
return 0
user = os.environ.get("SCP_USER", "your_user")
local_path = os.environ.get("SCP_LOCAL_FILE", "report.txt")
remote_path = os.environ.get("SCP_REMOTE_FILE", f"/home/{user}/report.txt")
command = ["scp", local_path, f"{user}@{host}:{remote_path}"]
try:
completed = subprocess.run(command, check=False)
except FileNotFoundError:
return 127
return completed.returncode
if __name__ == "__main__":
copy_file()
子程序的回傳碼為零,表示 scp 回報成功;非零值則表示傳輸指令失敗。在應用程式中,請檢查或傳遞該回傳碼,並處理 FileNotFoundError,以便區分找不到可執行程式與 SSH 或傳輸失敗。對可能包含使用者輸入的值,請避免使用 shell=True。若要執行遠端指令,請使用 SSH 程式庫,或另行謹慎建構 SSH 呼叫;subprocess.run() 本身只會啟動本機程序。若要將傳輸整合至 Python 程式碼中,請選擇 SCPClient;若偏好使用系統的 OpenSSH 用戶端及其設定,則可選擇 scp 可執行程式。