公众号:《Python编程时光》,作者:铭哥在使用Python写一些脚本的时候,在某些情况下,我们需要频繁的登录远程服务执行一个命令并返回一些结果。在shell环境下,我们是这样操作的。$sshpass-p${passwd}ssh-p${port}-l${user}-oStrictHostKeyChecking=noxx.xx.xx.xx"ls-l"然后你会发现你的输出有很多不需要,但有些信息无法去除(或许有办法,请留言交流),类似这个主机:xx.xx.xx.xx,端口:xxWarning:Permanentlyadded'[xx.xx.xx.xx]:xx'(RSA)到已知主机列表。登录失败:[Errno1]本服务器没有注册到rmp平台,请确认是否是cdn服务器。total4-rw-r--r--1rootroot239Mar302018admin-openrc对于直接使用shell命令执行命令的,可以直接使用管道,或者将标准输出重定向到文件即可获取命令执行返回的结果1.使用subprocess如果使用Python来做这件事,通常我们会马上想到使用一些命令执行库如os.popen、os.system、commands、subprocess来间接获取。但据我所知,这些库得到的输出不仅是标准输出,还有标准错误(也就是上面的冗余信息),所以每次输出需要清洗和格式化时,我们可以得到我们想要什么。你想要的数据。以subprocess为例,像这样importsubprocessssh_cmd="sshpass-p${passwd}ssh-p22-lroot-oStrictHostKeyChecking=noxx.xx.xx.xx'ls-l'"status,output=subprocess...如果避不开密)痛点2:干扰信息太多,数据清洗格式化相当麻烦痛点3:代码实现不够优雅(有点脏),可读性太差痛点4:SSH连接不能复用,只能执行一个连接痛点5:代码不能在所有平台上使用,只能在Linux和OSX上使用。为了解决这些问题,我在全网搜索了关于Pythonssh的文章,但是没有看到这方面的完整介绍。为此,我浏览了一个非常流行的Github项目:awesome-python-cn(https://github.com/BingmingWo...)。期望在这里找到一些关于远程连接的有用库。果然找到了两个sh.sshParamiko2。先用sh.ssh先介绍一下第一个,sh.sshsh是一个可以通过函数调用来完成Linxu/OSX系统命令的库,非常好用,有机会写一篇介绍。$python3-mpipinstallsh今天只介绍它的一个功能:ssh通常是在两台机器之间进行通信。为了方便起见,您可以设置免密登录,这样就不需要输入密码了。这段代码可以实现免密码登录,执行我们的命令ls-lfromshimportsshoutput=ssh("root@xx.xx.xx.xx","-p22","ls-l")print(output)但是有可能我们不想设置没有密码的互信。为了让这段代码更通用,我假设我们没有设置passwordfree,只能使用密码登录。问题来了。要输入密码,您必须使用交互式方法输入。如何在Python中实现它?原来ssh方法接收一个_out参数,可以是代表文件路径的字符串,也可以是文件对象(或类文件对象),也可以是回调函数,表示当有标准输出时,会将被调用以将输出传递给此函数。这很容易处理。只要我识别出password:这个词,我就会将我的密码写入标准输入。完整代码如下:importsysfromshimportsshaggregated=""defssh_interact(char,stdin):globalaggregatedsys.stdout.write(char.encode())sys.stdout.flush()aggregated+=charifaggregated.endswith("密码:"):stdin.put("you_password\n")output=ssh("root@xx.xx.xx.xx","-p22","ls-l",_tty_in=True,_out_bufsize=0,_out=ssh_interact)print(output)这是官方文档(http://amoffat.github.io/sh/t...)给出的一些信息,写的一个demo。尝试运行后发现程序会一直运行,永远不会返回,永远不会退出,回调函数永远不会进入。通过debug查看源码,还是没找到问题所在,于是上Github搜索。原来这个问题在2017年就已经存在了,一直到2020年才修复,好像用sh.ssh的人不多,于是又“跟进”了,希望能得到回复。只有在需要输入密码时才会出现上述问题。如果你设置了机器之间的互信,就没有问题。为了体验使用sh.ssh的效果,我设置了无密码机器互信,然后使用如下代码。fromshimportsshmy_server=ssh.bake("root@xx.xx.xx.xx","-p22")#相当于执行了一次登录,执行了一次命令,执行完就退出了print(my_server.ls())#sleep时可以手动登录服务器,使用top查看当前连接了多少终端time.sleep(5)#再次执行该命令时,登录终端数为+1、执行后会-1print(my_server.ifconfig())惊讶的发现使用bake,my_server.ls()和my_server.ifconfig()看似通过同一个ssh连接执行了两次命令,但实际上,你可以在远程机器上,执行top命令,查看连接终端的变化,会先+1,再-1,说明这两个命令的执行是通过两个连接实现的。从这点来看,使用sh.ssh可以解决痛点1(如果能解决以上问题)、痛点2、痛点3。但是还是不能复用ssh连接,还是不方便,不是我理想中的最佳解决方案。最重要的一点是sh模块只支持Linxu/OSX,你必须使用它的兄弟库——Windows上的pbs。然后又去pypi看了一下pbs,一直“年久失修”,没人维护。至此,我离“棋子”还差最后一根稻草。3、使用paramiko抱着最后的希望,尝试使用paramiko库,终于在paramiko中找到了本该属于Python的优雅。可以通过以下命令安装$python3-mpipinstallparamiko然后接下来介绍几种常用的ssh登录方法方法一:根据用户名和密码进行sshclient登录然后可以参考下面的代码,在RemoteLinux/OSX系统下的连接importparamikossh=paramiko.SSHClient()#允许连接到known_hosts文件中没有的主机ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())#建立连接ssh.connect("xx.xx.xx.xx",username="root",port=22,password="you_password")#使用此连接执行命令ssh_stdin,ssh_stdout,ssh_stderr=ssh.exec_command("ls-l")#获取输出print(ssh_stdout.read())#关闭连接ssh.close()方法二:基于用户名和密码的传输方式登录方法方法一是传统的连接服务器,执行命令,关闭的操作。多个操作需要多个连接,连接无法复用【痛点四】。有时需要登录服务器进行多项操作,如执行命令、上传/下载文件等。方法一无法实现,可以使用transport方法。importparamiko#建立连接trans=paramiko.Transport(("xx.xx.xx.xx",22))trans.connect(username="root",password="you_passwd")#指定sshclient对象的传输如上transssh=paramiko.SSHClient()ssh._transport=trans#其余同上ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh_stdin,ssh_stdout,ssh_stderr=ssh.exec_command("ls-l")print(ssh_stdout.read())#关闭连接trans.close()方法三:基于公钥的SSHClient登录keyimportparamiko#指定本地RSA私钥文件#如果创建密钥对时设置了密码,password为设置的密码,如果不需要指定密码参数pkey=paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa',password='12345')#建立连接ssh=paramiko.SSH客户端()ssh。connect(hostname='xx.xx.xx.xx',port=22,username='you_username',pkey=pkey)#执行命令stdin,stdout,stderr=ssh.exec_command('ls-l')#put结果到stdout,如果有错误,会放到stderr中RSA私钥文件#如果在创建密钥对的时候设置了密码,password就是设置的密码,如果没有则不需要指定密码参数pkey=paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa',password='12345')#建立连接trans=paramiko.Transport(('xx.xx.xx.xx',22))trans.connect(username='you_username',pkey=pkey)#指定sshclient对象的transport如上transssh=paramiko.SSHClient()ssh._transport=trans#执行命令,同传统方法stdin,stdout,stderr=ssh.exec_command('df-hl')print(stdout.read().decode())#关闭连接trans.close()以上四个方法可以帮你实现远程登录服务器执行命令,如果你需要重用连接:一个连接执行多个命令,可以使用方法2和方法4,记得关闭连接,实现sftp文件传输。同时paramiko是ssh的完美解决方案。很专业,也可以用来实现sftp文件传输。importparamiko#实例化一个trans对象#实例化一个transport对象trans=paramiko.Transport(('xx.xx.xx.xx',22))#建立连接trans.connect(username='you_username',password='you_passwd')#实例化一个sftp对象,指定连接的通道sftp=paramiko.SFTPClient.from_transport(trans)#发送文件sftp.put(localpath='/tmp/11.txt',remotepath='/tmp/22.txt')#下载文件sftp.get(remotepath='/tmp/22.txt',localpath='/tmp/33.txt')trans.close()这里Paramiko赢了,但是还有一个痛点我们没有提到,它是多平台的,我们在谈论Windows,这里有好事也有坏事。好处是:paramiko支持windows。不好的是:需要做很多复杂的准备工作,可以google一下解决,但是建议还是放弃吧,坑太深了。4、经过一些对比和一些例子,可以看出Paramiko是一款专业省心的ssh工具。个人认为Paramiko模块是运维人员必学的模块之一。如果你恰好需要用Python代码实现ssh到远程服务器获取一些信息,那么我向你推荐Paramiko。最后,希望这篇文章能对你有所帮助。5.参考链接https://github.com/paramiko/p...http://docs.paramiko.orghttps://www.liujiangblog.com/...本文由ArtiPub自动发布发帖平台