#科技一把手#用Python远程登陆服务器的最佳实践( 二 )


完整代码如下:
importsysfromshimportsshaggregated=''''defssh_interact(char,stdin):globalaggregatedsys.stdout.write(char.encode)sys.stdout.flushaggregated+=charifaggregated.endswith(''password:''):stdin.put(''you_passwordn'')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/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息 , 写的一个demo 。
尝试运行后 , 发现程序会一直在运行中 , 永远不会返回 , 不会退出 , 回调函数也永远不会进入 。
通过调试查看源代码 , 仍然查不到问题所在 , 于是去Github上搜了下 , 原来在2017年就已经存在这个问题了 , 到现在2020年了还没有修复 , 看来使用sh.ssh的人并不多 , 于是我又“追问”了下 , 期望能得到回复 。
#科技一把手#用Python远程登陆服务器的最佳实践
文章图片
以上这个问题 , 只有在需要输入密码才会出现 , 如果设置了机器互信是没有问题的 。
为了感受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可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三 。
但是它仍然无法复用ssh连接 , 还是不太方便 , 不是我理想中的最佳方案 。
最重要的一点是 , sh这个模块 , 仅支持Linxu/OSX , 在Windows你得使用它的兄弟库-pbs , 然后我又去pypi看了一眼pbs , 已经“年久失修” , 没人维护了 。
#科技一把手#用Python远程登陆服务器的最佳实践
文章图片
至此 , 我离“卒” , 就差最后一根稻草了 。
#科技一把手#用Python远程登陆服务器的最佳实践
文章图片
使用paramiko
带着最后一丝希望 , 我尝试使用了paramiko这个库 , 终于在paramiko这里 , 找回了本应属于Python的那种优雅 。
你可以通过如下命令去安装它
$python3-mpipinstallparamiko
然后接下来 , 就介绍几种常用的ssh登陆的方法
方法1:基于用户名和密码的sshclient方式登录
然后你可以参考如下这段代码 , 在Linux/OSX系统下进行远程连接
importparamikossh=paramiko.SSHClient#允许连接不在know_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
方法2:基于用户名和密码的transport方式登录
方法1是传统的连接服务器、执行命令、关闭的一个操作 , 多个操作需要连接多次 , 无法复用连接[痛点四] 。
有时候需要登录上服务器执行多个操作 , 比如执行命令、上传/下载文件 , 方法1则无法实现 , 那就可以使用transport的方法 。


推荐阅读