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 相关文章推荐
Python缩进和冒号详解
Jun 01 Python
Django contenttypes 框架详解(小结)
Aug 13 Python
Python 实现某个功能每隔一段时间被执行一次的功能方法
Oct 14 Python
python虚拟环境迁移方法
Jan 03 Python
Python3.4学习笔记之列表、数组操作示例
Mar 01 Python
Python 读取串口数据,动态绘图的示例
Jul 02 Python
Django 迁移、操作数据库的方法
Aug 02 Python
python3 配置logging日志类的操作
Apr 08 Python
numpy 矩阵形状调整:拉伸、变成一位数组的实例
Jun 18 Python
从python读取sql的实例方法
Jul 21 Python
Scrapy 配置动态代理IP的实现
Sep 28 Python
Django自带的用户验证系统实现
Dec 18 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实现邮件群发的源码
2013/06/18 PHP
彻底删除thinkphp3.1案例blog标签的方法
2014/12/05 PHP
php 修改上传文件大小限制实例详解
2016/10/23 PHP
删除PHP数组中的重复元素的实现代码
2017/04/10 PHP
PHP实现防盗链的方法分析
2017/07/25 PHP
Laravel框架实现的使用smtp发送邮件功能示例
2019/03/12 PHP
jquery自定义属性(类型/属性值)
2013/05/21 Javascript
JS测试显示屏分辨率以及屏幕尺寸的方法
2013/11/22 Javascript
JS实现模仿微博发布效果实例代码
2013/12/16 Javascript
js跳转页面方法总结
2014/01/29 Javascript
javascript的解析执行顺序在各个浏览器中的不同
2014/03/17 Javascript
基于豆瓣API+Angular开发的web App
2015/01/02 Javascript
解决js图片加载时出现404的问题
2020/11/30 Javascript
Angularjs注入拦截器实现Loading效果
2015/12/28 Javascript
封装好的javascript前端分页插件pagination
2016/01/04 Javascript
JS实现PC手机端和嵌入式滑动拼图验证码三种效果
2017/02/15 Javascript
js数字舍入误差以及解决方法(必看篇)
2017/02/28 Javascript
jquery replace方法去空格
2017/05/08 jQuery
Vue-cli proxyTable 解决开发环境的跨域问题详解
2017/05/18 Javascript
JS滚动到指定位置导航栏固定顶部
2017/07/03 Javascript
JavaScript hasOwnProperty() 函数实例详解
2017/08/04 Javascript
微信小程序实现全国机场索引列表
2018/01/31 Javascript
Vue.js 中 axios 跨域访问错误问题及解决方法
2018/11/21 Javascript
在vue项目中使用Jquery-contextmenu插件的步骤讲解
2019/01/27 jQuery
Python输出9*9乘法表的方法
2015/05/25 Python
解决python3中解压zip文件是文件名乱码的问题
2018/03/22 Python
TensorFLow 不同大小图片的TFrecords存取实例
2020/01/20 Python
python如何更新包
2020/06/11 Python
英国领先品牌手动工具和电动工具供应商:Tooled Up
2018/11/24 全球购物
TecoBuy澳大利亚:在线电子和小工具商店
2020/06/25 全球购物
在阿联酋购买翻新手机和平板电脑:Teckzu
2021/02/12 全球购物
计算机大学生的自我评价
2013/10/15 职场文书
销售助理岗位职责
2014/02/21 职场文书
大学生学习2014全国两会心得体会
2014/03/13 职场文书
MySQL的join buffer原理
2021/04/29 MySQL
python开发飞机大战游戏
2021/07/15 Python