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 urllib模块urlopen()与urlretrieve()详解
Nov 01 Python
Python的ORM框架SQLObject入门实例
Apr 28 Python
一则python3的简单爬虫代码
May 26 Python
Python自动连接ssh的方法
Mar 07 Python
Python使用爬虫猜密码
Feb 19 Python
详解Python的Django框架中manage命令的使用与扩展
Apr 11 Python
在Python的Flask框架中构建Web表单的教程
Jun 04 Python
Django实现的自定义访问日志模块示例
Jun 23 Python
Windows下的Python 3.6.1的下载与安装图文详解(适合32位和64位)
Feb 21 Python
详解重置Django migration的常见方式
Feb 15 Python
python匿名函数lambda原理及实例解析
Feb 07 Python
浅谈keras中的目标函数和优化函数MSE用法
Jun 10 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
全国FM电台频率大全 - 8 黑龙江省
2020/03/11 无线电
模拟OICQ的实现思路和核心程序(二)
2006/10/09 PHP
php以post形式发送xml的方法
2014/11/04 PHP
PHP小程序支付功能完整版【基于thinkPHP】
2019/03/26 PHP
JavaScript中的连字符详解
2013/11/28 Javascript
js处理php输出时间戳对不上号的解决方法
2014/06/20 Javascript
js遍历子节点子元素附属性及方法
2014/08/19 Javascript
js实现鼠标感应图片展示的方法
2015/02/27 Javascript
css如何让浮动元素水平居中
2015/08/07 Javascript
js验证身份证号有效性并提示对应信息
2015/10/19 Javascript
Node.js中npm常用命令大全
2016/06/09 Javascript
jQuery事件处理的特征(事件命名机制)
2016/08/23 Javascript
详解vue-cli开发环境跨域问题解决方案
2017/06/06 Javascript
提升node.js中使用redis的性能遇到的问题及解决方法
2018/10/30 Javascript
vue实现将数据存入vuex中以及从vuex中取出数据
2019/11/08 Javascript
[01:53]DOTA2超级联赛专访Zhou 五年职业青春成长
2013/05/29 DOTA
[44:40]Serenity vs Pain 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python实现随机密码字典生成器示例
2014/04/09 Python
Python Sqlite3以字典形式返回查询结果的实现方法
2016/10/03 Python
TensorFlow如何实现反向传播
2018/02/06 Python
Django项目实战之用户头像上传与访问的示例
2018/04/21 Python
python+opencv实现阈值分割
2018/12/26 Python
使用IDLE的Python shell窗口实例详解
2019/11/19 Python
python GUI库图形界面开发之PyQt5信号与槽事件处理机制详细介绍与实例解析
2020/03/08 Python
python工具快速为音视频自动生成字幕(使用说明)
2021/01/27 Python
CSS3实现自定义Checkbox特效实例代码
2017/04/24 HTML / CSS
通过css3的filter滤镜改变png图片的颜色的示例代码
2020/05/06 HTML / CSS
用HTML5制作视频拼图的教程
2015/05/13 HTML / CSS
西班牙多品牌鞋店连锁店:Krack
2018/11/30 全球购物
护士实习生自我鉴定范文
2013/12/10 职场文书
护士找工作求职信
2014/07/02 职场文书
争先创优心得体会
2014/09/12 职场文书
五好家庭申报材料
2014/12/20 职场文书
挂靠协议书
2015/01/27 职场文书
读鲁迅先生的经典名言
2019/08/20 职场文书
理解深度学习之深度学习简介
2021/04/14 Python