python 第三方库paramiko的常用方式


Posted in Python onFebruary 20, 2021

介绍

paramiko是什么可以参考其他人的博客或文章,这里不再赘述,直入正题。

本次测试的版本信息如下:

  • python 3.9
  • paramiko 2.7.2
  • centos 8

三种常用方式

paramiko 的三种常用方式如下:

  • 使用密码进行登录
  • 使用密钥免密码登录
  • SFTP 传输文件

其中最割裂的就是SFTP 传输文件,很多文章登陆使用SSHClient类,传输文件使用Transport类,我也是这样用了很长时间。

如果你也是这么用的,你没有啥想法吗?用python就是节约心智,怎么一个变形还能出来两种东西呢,没有办法统一吗?

网上的统一就是实例化Transport类然后实例化SSHClient类,再把实例化的Transport类添加到实例化SSHClient类。总是有一种别扭的感觉。

重点:查看源码可以发现,SSHClient类直接提供了 SFTP 传输文件的实例化方法,直接用就行了,世界顿时清爽了很多

使用密码进行登录

import paramiko

hostname = 'localhost'
port = 22
username = 'aaron'
# 看密码就知道我是用的redhat系linux系统
password = 'redhat'

# 实例化SSHClient类
ssh = paramiko.SSHClient()
# 远程主机没有本地密钥时的处理规则,主要有三个
# AutoAddPolicy:直接建立连接,不进行yes/no的确认
# WarningPolicy:直接建立连接,但是会提示是新连接
# RejectPolicy:拒绝未知的连接,依赖系统密钥的信息。默认选项。
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接到服务器
ssh.connect(hostname, port, username, password)

# 执行命令,获取标准输入、标准输出、标准错误输出,均为流式输入输出
# 函数原型为 exec_command(self, command, bufsize=-1, timeout=None, get_pty=False, environment=None, )
# 理论上可以通过标准输入,也就是下面的额stdin变量完成连续输入
# 同时参数中有布尔型参数 get_pty 可以指定是否获取 tty 通道,这样阻塞输入,比如sudo输入密码什么的都能做。貌似就可以做成你想要的任何东西。
# 但是以上两点没有验证,貌似比较麻烦,我太懒了-_-|||
#
# 另外,exec_command方法每次都是新开一个通道执行命令,执行完成后状态消失。SSHClient类还提供一个invoke_shell方法,这个方法可以连续输入命令。
# 这两个的区别主要是 invoke_shell使用SSH shell通道,而exec_command使用SSH exec通道。
# shell通道就是常用的终端软件登陆的通道,登陆变量都会进行加载比如 ~/bashrc 等
# 而 exec通道 则不进行加载登陆文件,相当于linux桌面系统上右键开terminal一样。
# 如果你还是不懂,没关系,invoke_shell nb就完事了
stdin, stdout, stderr = ssh.exec_command('df')
# 打印输出
print(stdout.read().decode())
# 不要忘记关闭连接
ssh.close()

使用密钥免密码登录

这里使用密钥文件,但是为了一般情况,我给密钥文件设置了密码,如果你只是想免密码,不设置密码即可.

在客户机上生成密钥对,将公钥传递给服务器

ssh-keygen -t rsa # 这里设置密码为redhat_rsa,这里是给密钥设置密码,如果想免密,不设置密码即可
ssh-copy-id -i ~/.ssh/id_rsa.pub aaron@localhost
import paramiko

hostname = 'localhost'
port = 22
username = 'aaron'
# 这里是密钥文件的密码
password = 'redhat_rsa'
# 密钥文件的位置,可以是列表,paramiko会把列表里文件顺序尝试,登陆上位置
private_key_path = '/home/aaron/.ssh/id_rsa'

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 这里网上很多先设置pkey传入,但是直接传文件路径也可以,还简单。
# 我使用的和网上不同,另一个版本请自行搜索,资料n多
# 如果没有密钥,则不需添加password
# look_for_keys默认为True,就是会找你 .ssh 目录下有没有合适的密钥文件
# 也就是说如果密钥文件存在,但是你传 key_filename 时传错了,不影响,paramiko已经替你想好了,这才是正经 python 应有的待遇,舒服!
ssh.connect(hostname, port, username=username, password=password, key_filename=private_key_path, look_for_keys=False)

stdin, stdout, stderr = ssh.exec_command('ip a')
print(stdout.read().decode())
ssh.close()

SFTP 传输文件

import paramiko

hostname = 'localhost'
port = 22
username = 'aaron'
password = 'redhat'

# 还是SSHClient登陆,以上两种方式都可以。
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname, port, username, password)

# 重头戏,直接使用打开方法即可
sftp = ssh.open_sftp()

# do something
# 从这里到下面的ssh.close()为止都是sftp能做的事情,具体能做啥,请看下一个代码段,这里只列举上传(put) 下载(get) 文件,这两个也比较重要

# 回调函数,没想到吧,上传下载还能有回调函数
# 参数一定,都是传入的两个size,int型数据
# size 已传输文件累计大小
# file_size 文件总大小
def callback(size, file_size):
 print(f"目前传输文件比例: {size} / {file_size}")

# 上传文件,参数都给你们了,看看啥意思就行了
# 主要就是这个confirm, 如果定义会检测一下上传到服务器文件大小和本地大小是否一致,默认False
stat = sftp.put(localpath='/tmp/s.avi', remotepath='/tmp/a.avi', callback=callback, confirm=True)
print(stat)

# 下载文件,同样参数都给你们了,看看啥意思就行了
sftp.get(localpath='/tmp/s.avi', remotepath='/tmp/a.avi', callback=callback)

ssh.close()

stfp 能 do 的 something

# 列出当前路径下有什么文件,默认path="."
print(sftp.listdir())

# 列出当前路径下文件属性,默认path="."
attrs = sftp.listdir_attr()
print(attrs)
print("="*20)
# listdir函数就是遍历的每个属性的filename
print(attrs[0].filename)
print(attrs[0].longname)
print(attrs[0].st_atime)
print(attrs[0].st_mtime)
print(attrs[0].st_gid)
print(attrs[0].st_uid)
print(attrs[0].st_mode)

# 就是 listdir_attr 的迭代器版本
attrs = sftp.listdir_iter()
print("="*20)
for i in attrs:
 print(i.filename)
 print(i.longname)
 print(i.st_atime)
 print(i.st_mtime)
 print(i.st_gid)
 print(i.st_uid)
 print(i.st_mode)

# 和内置open用法基本相同,只不过是打开外部文件
with sftp.open("a.txt", "w") as f:
 f.write("aaa")

# 删除文件,只能删除文件,删除目录使用rmdir函数。文件不存在则报错。
sftp.remove("/home/aaron/a.txt")

# 文件改名,类似于move
sftp.rename("/home/aaron/as.txt", "/tmp/soon.txt",)

# 符合posix标准的改名,没有测试
sftp.posix_rename("/home/aaron/as.txt", "/tmp/soon.txt",)

# 新建目录
sftp.mkdir("/home/aaron/as")

# 删除目录,类似rmdir,删除的必须为空目录
sftp.rmdir("/home/aaron/as")

# 返回单个文件的attr信息,如果是软连接则直接返回真实文件信息
stat = sftp.stat("/tmp/soon.txt")
print(stat)

# 测试和stat差不多,如果是软链接则返回软链接文件信息
stat = sftp.lstat("/tmp/soon.txt")
print(stat)

# 修改权限,权限为八进制数,需要把权限换算为十进制数。比如下面的例子就是权限333
sftp.chmod("/tmp/soon.txt", 219)

# 修改属主和属组,属主和属组为gid和uid表示。需要有权限。
sftp.chown("/tmp/soon.txt", 0, 0)

# 设置atime和mtime,如果传入None,则设置为当前时间。否则必须传入两个元素的元组或数组,分别为 (atime, mtime)
sftp.utime("/tmp/soon.txt", None)
import time
sftp.utime("/tmp/soon.txt", (time.time(), time.time()))

# 读取软链接指定的目标
print(sftp.readlink("/etc/rc.local"))

# 读取软连接制定目标的绝对路径
print(sftp.normalize("/etc/rc.local"))

# 切换工作路径。SFTP没有工作路径的概念,但是paramiko进行了模拟。如果设置了路径,所有的相对路径都是根据这个路径来的。如果想要切换回去传入None即可。
sftp.chdir("/tmp")

# 获取当前的工作路径。如果没有使用chdir切换过,则会返回None
print(sftp.getcwd())

terminal demo

自己一直想做一个类似xshell的东西,尤其是mac本的iterm或者iterm2是啥垃圾,还被吹的不行不行的,是没用过好东西吗。

但是每次执行exec_command都会从家目录开始,无法切换目录,十分不方便。一直没有啥进展,知道遇到了 invoke_shell ,一切看起来都有了些可能。

import time
from threading import Thread

import paramiko


# 接收消息并打印的函数
# 返回的消息会分成好几段,如果只是发送命令后直接打印是打印不全的,这里直接循环检测缓冲区,有结果就打印。
def recv_and_print(channel):
 # 定义全局变量,recv_func_flag 此接收函数退出标志,cmd 当前执行命令
 global recv_func_flag, cmd
 while recv_func_flag:
 # 如果此次命令是exit并且退出完成,则设置退出标志
 if cmd == "exit" and channel.exit_status_ready():
  # 打印退出状态,为int型数字
  print(f"此次退出状态:{channel.recv_exit_status()}")
  # 退出标志置为假
  recv_func_flag = False
 # 吐过缓冲区有数据
 if channel.recv_ready():
  # 接收数据
  response = channel.recv(1024).decode().strip()
  # 需要注意的是接收的数据会把传入的命令也返回一遍,这里我们只保留自己打在屏幕上的,不要传回的,所以传回的数据如果和命令相同则不打印,略过
  if response != cmd:
  print(response, end="")

 print("接收函数退出......")


# 定义全局变量
recv_func_flag = True
cmd = ""

# ssh登陆,老一套东西
hostname = 'localhost'
port = 22
username = 'aaron'
password = 'redhat010;'

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname, port, username, password)

# 获取invoke_shell
invoke_shell = ssh.invoke_shell()
# 接收函数使用另一个线程运行,因为和当前主线程一同退出,所以不需要join方法
t = Thread(target=recv_and_print, args=(invoke_shell,))
t.start()

# 主线程退出标志
flag = True
while flag:
 # 输入命令
 cmd = input()
 # 输入命令必须有回车才会执行,这里我发送的是linux命令,\r之后能执行命令,如果系统不同,需要测试\r\n等回车字符
 invoke_shell.send(f"{cmd}\r")
 # 如果命令是exit则退出循环
 if cmd == "exit":
 flag = False

# 检测接收函数已经退出,这里停止0.5s是因为退出命令发送给服务器,服务器会返回注销的信息,之后检测接收函数才会完全退出,认为设置了一个等待时间,这个时间因为是本机,设置的相对不长,如果是其他主机,需要根据网络以及超时情况进行设置
while recv_func_flag:
 time.sleep(0.5)

# 别忘了关闭ssh
ssh.close()

这个demo直接运行然后输入命令即可,就像是使用terminal直接登陆一样。

ll等定义的alias也是能使用的。

但是双击tab ctrl+c 等没有实现,留待诸君完善吧

这个demo目前还有一些问题。时间原因也懒得解决了,以后用到的时候再深入探究吧。

  • 换行总是有问题,时好时坏,感觉每次发送数据有时命令、结果、信息提示符有时合并发送,有时分开发送,没有啥必然规律。也许和linux发行版有关?和tcp通信有关?目前原因不明(具体现象请自行测试)
  • 显示信息使用了 print(response, end="") 退出时也使用相同的显示命令,到时系统注销的显示信息和函数的提示信息"接收函数退出......"拼接在一起了(对啊,提示信息之前我可以加个回车啊,哎呀,不再测试了,太累了)
  • su命令也可以正常执行,输入密码啥的不影响,但是密码会明文显示。。。。。
  • 接上条,su退出成问题,su退出使用exit,整个函数就退出了。算了,不修复了/-_-\

到此这篇关于python 第三方库paramiko的文章就介绍到这了,更多相关python 第三方库paramiko内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python3.3实现乘法表示例
Feb 07 Python
Django中URLconf和include()的协同工作方法
Jul 20 Python
Python中创建字典的几种方法总结(推荐)
Apr 27 Python
python下实现二叉堆以及堆排序的示例
Sep 29 Python
详解K-means算法在Python中的实现
Dec 05 Python
Python实现一个Git日志统计分析的小工具
Dec 14 Python
python 获取url中的参数列表实例
Dec 18 Python
Python爬虫设置代理IP(图文)
Dec 23 Python
使用遗传算法求二元函数的最小值
Feb 11 Python
Python numpy矩阵处理运算工具用法汇总
Jul 13 Python
Python使用正则表达式实现爬虫数据抽取
Aug 17 Python
解决Pytorch dataloader时报错每个tensor维度不一样的问题
May 28 Python
Python中Qslider控件实操详解
Feb 20 #Python
python基于selenium爬取斗鱼弹幕
Feb 20 #Python
Python爬虫+Tkinter制作一个翻译软件的示例
Feb 20 #Python
python爬虫用request库处理cookie的实例讲解
Feb 20 #Python
python 多线程爬取壁纸网站的示例
Feb 20 #Python
python 制作网站小说下载器
Feb 20 #Python
如何用python爬取微博热搜数据并保存
Feb 20 #Python
You might like
支持中文的PHP按字符串长度分割成数组代码
2015/05/17 PHP
javascript来定义类的规范小结
2010/11/19 Javascript
基于jquery实现的上传图片及图片大小验证、图片预览效果代码
2011/04/12 Javascript
js实现杯子倒水问题自动求解程序
2013/03/25 Javascript
JQuery中使用on方法绑定hover事件实例
2014/12/09 Javascript
JS交换变量的方法
2015/01/21 Javascript
jQuery实现点击按钮弹出可关闭层的浮动层插件
2015/09/19 Javascript
客户端验证用户名和密码的方法详解
2016/06/16 Javascript
关于JS中setTimeout()无法调用带参函数问题的解决方法
2016/06/21 Javascript
使用JavaScript判断用户输入的是否为正整数(两种方法)
2017/02/05 Javascript
javaScript日期工具类DateUtils详解
2017/12/08 Javascript
JS 实现百度搜索功能
2018/02/01 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
初探Vue3.0 中的一大亮点Proxy的使用
2018/12/06 Javascript
抖音上用记事本编写爱心小程序教程
2019/04/17 Javascript
微信小程序 扭蛋抽奖机css3动画实现详解
2019/07/19 Javascript
Javascript原型链及instanceof原理详解
2020/05/25 Javascript
Jupyter安装nbextensions,启动提示没有nbextensions库
2020/04/23 Python
python 产生token及token验证的方法
2018/12/26 Python
简单了解python PEP的一些知识
2019/07/13 Python
python利用itertools生成密码字典并多线程撞库破解rar密码
2019/08/12 Python
python 字典有序并写入json文件过程解析
2019/09/30 Python
Python基本语法之运算符功能与用法详解
2019/10/22 Python
python 中的paramiko模块简介及安装过程
2020/02/29 Python
在python中使用pyspark读写Hive数据操作
2020/06/06 Python
Window10上Tensorflow的安装(CPU和GPU版本)
2020/12/15 Python
python中doctest库实例用法
2020/12/31 Python
MyBag中文网:英国著名的时尚包袋电商零售网站
2020/07/31 全球购物
职业生涯规划书基本格式
2014/01/06 职场文书
爷爷追悼会答谢词
2014/01/24 职场文书
教师自我鉴定范文
2014/03/20 职场文书
社区文艺活动方案
2014/08/19 职场文书
建国大业观后感800字
2015/06/01 职场文书
培训班开班主持词
2015/07/02 职场文书
Java Optional<Foo>转换成List<Bar>的实例方法
2021/06/20 Java/Android
利用Java连接Hadoop进行编程
2022/06/28 Java/Android