python上传文件到Linux&从Linux下载文件&在Linux执行命令
python上传文件到Linux&从Linux下载文件&在Linux执行命令
直接上代码
import os.path
import stat
from pathlib import PurePosixPath
from typing import Dict
from paramiko.client import SSHClient, AutoAddPolicy
from paramiko.sftp_attr import SFTPAttributes
from paramiko.sftp_client import SFTPClient
class SSH:
def __init__(self, ip: str, port: int, username: str, password: str, timeout: int):
self.ip = ip
self.port = port
self.username = username
self.password = password
self.timeout = timeout
self.client = SSHClient()
self._conn = False
self.sftp: [SFTPClient, None] = None
def conn(self):
ip = self.ip
port = self.port
username = self.username
timeout = self.timeout
password = self.password
self.client.set_missing_host_key_policy(AutoAddPolicy())
self.client.connect(ip, port, username, password=password, timeout=timeout)
self._conn = True
def close(self):
if self.sftp:
self.sftp.close()
if self._conn:
self.client.close()
def exec_cmd(self, cmd: str):
if not self._conn:
self.conn()
_, out, err = self.client.exec_command(cmd)
out = out.read().decode()
err = err.read().decode()
return out, err
def activation_sftp(self):
self.sftp = SFTPClient.from_transport(self.client.get_transport())
def download_file(self, download_info: Dict[str, str]):
# local_path 必须拼接到文件名
if not self.sftp:
self.activation_sftp()
sftp: SFTPClient = self.sftp
for k in download_info:
path = PurePosixPath(k)
ret: SFTPAttributes = sftp.stat(str(path))
mode = ret.st_mode
is_file = stat.S_ISREG(mode)
assert is_file, f'远程路径不是一个文件:{path}'
for k, v in download_info.items():
v = os.path.abspath(v)
if not os.path.exists(os.path.dirname(v)):
os.makedirs(os.path.dirname(v))
sftp.get(k, v)
def upload_file(self, upload_info: Dict[str, str]):
"""
:param upload_info: 上传的字典 key是本地的文件 value是远程Linux的路径 必须写到具体文件名
:return:
"""
for k in upload_info:
path = os.path.abspath(k)
assert os.path.isfile(path), "本地源文件不存在"
if not self.sftp:
self.activation_sftp()
sftp: SFTPClient = self.sftp
for k, v in upload_info.items():
k = os.path.abspath(k)
v = PurePosixPath(v)
suffix = v.suffix
assert suffix, "保存路径必须具体到文件名"
print(f"upload {k} ===> {v}")
self.exec_cmd(f'mkdir -p {v.parent}') # 创建文件夹
ret = sftp.put(k, str(v)) # 会覆盖已有的文件
print(f"upload ret:{ret}")
def main():
obj = SSH("172.17.140.17", 22, "root", "abc123", 60)
obj.conn()
out, err = obj.exec_cmd("df -h") # 执行命令
print(f'out:{out}')
print(f"err:{err}")
print()
out, err = obj.exec_cmd("free -h") # 执行命令
print(f'out:{out}')
print(f"err:{err}")
print()
path = "/tmp/tmp1/my_tar.tar.gz"
p = PurePosixPath(path) # 声明这个是Linux路径
obj.exec_cmd(f'mkdir -p {p.parent}') # 创建文件夹
out, err = obj.exec_cmd(f"tar cfvzP {path} /home/tmp/data") # 压缩文件夹
print(f'out:{out}')
print(f"err:{err}")
upload_info = {
"a.txt": "/tmp/aaa/a.txt", # 目标路径必须是文件名 而不能是文件夹
"b.txt": "/tmp/aaa/b.txt" # 目标路径必须是文件名 而不能是文件夹
}
obj.upload_file(upload_info)
download_info = {
"/tmp/aaa/a.txt": "a.txt", # 源路径必须是个文件
"/tmp/aaa/b.txt": "cc/vv/a" # 源路径必须是个文件
}
obj.download_file(download_info)
obj.close()
if __name__ == '__main__':
main()
解析
- 通过SSHClient()创建SSH客户端
- 通过self.client.connect连接到Linux
- 通过SFTPClient.from_transport(self.client.get_transport())创建sftp客户端
- 上传时会在Linux自动创建文件夹
- 下载时先判断Linux中是否存在要下载的文件
- 目前只支持上传和下载文件