当前位置:脚本大全 > > 正文

pythonshell入门教程(python获取交互式ssh shell的方法)

时间:2022-03-31 00:59:39类别:脚本大全

pythonshell入门教程

python获取交互式ssh shell的方法

更新,最近在学unix环境编程,了解一下进程的创建过程,用最原始的方式实现了一个ssh命令的执行。

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • #coding=utf8
  •  
  • '''
  • 用python实现了一个简单的shell,了解进程创建
  • 类unix 环境下 fork和exec 两个系统调用完成进程的创建
  • '''
  •  
  • import sys, os
  •  
  •  
  • def myspawn(cmdline):
  •  argv = cmdline.split()
  •  if len(argv) == 0:
  •   return
  •  program_file = argv[0]
  •  pid = os.fork()
  •  if pid < 0:
  •   sys.stderr.write("fork error")
  •  elif pid == 0:
  •   # child
  •   os.execvp(program_file, argv)
  •   sys.stderr.write("cannot exec: "+ cmdline)
  •   sys.exit(127)
  •  # parent
  •  pid, status = os.waitpid(pid, 0)
  •  ret = status >> 8 # 返回值是一个16位的二进制数字,高8位为退出状态码,低8位为程序结束系统信号的编号
  •  signal_num = status & 0x0F
  •  sys.stdout.write("ret: %s, signal: %s\n" % (ret, signal_num))
  •  return ret
  •  
  •  
  • def ssh(host, user, port=22, password=None):
  •  if password:
  •   sys.stdout.write("password is: '%s' , plz paste it into ssh\n" % (password))
  •  cmdline = "ssh %s@%s -p %s " % (user, host, port)
  •  ret = myspawn(cmdline)
  •  
  •  
  • if __name__ == "__main__":
  •  host = ''
  •  user = ''
  •  password = ''
  •  ssh(host, user, password=password)
  • 最近在做一个项目,需要在客户端集成一个交互式ssh功能,大概就是客户端跟服务器申请个可用的机器,服务端返回个ip,端口,密码, 然后客户端就可以直接登录到机器上操做了。该程序基于paramiko模块。

    经查找,从paramiko的源码包demos目录下,可以看到交互式shell的实现,就是那个demo.py。但是用起来有些bug,于是我给修改了一下interactive.py(我把windows的代码删掉了,剩下的只能在linux下用)。代码如下:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • #coding=utf-8
  • import socket
  • import sys
  • import os
  • import termios
  • import tty
  • import fcntl
  • import signal
  • import struct
  • import select
  •  
  • now_channel = None
  •  
  • def interactive_shell(chan):
  •  posix_shell(chan)
  •  
  •  
  • def ioctl_GWINSZ(fd):
  •  try:
  •   cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'aaaa'))
  •  except:
  •   return
  •  return cr
  •  
  •  
  • def getTerminalSize():
  •  cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  •  return int(cr[1]), int(cr[0])
  •  
  •  
  • def resize_pty(signum=0, frame=0):
  •  width, height = getTerminalSize()
  •  if now_channel is not None:
  •   now_channel.resize_pty(width=width, height=height)
  •  
  •  
  •  
  • def posix_shell(chan):
  •  global now_channel
  •  now_channel = chan
  •  resize_pty()
  •  signal.signal(signal.SIGWINCH, resize_pty) # 终端大小改变时,修改pty终端大小
  •  stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) # stdin buff置为空,否则粘贴多字节或者按方向键的时候显示不正确
  •  fd = stdin.fileno()
  •  oldtty = termios.tcgetattr(fd)
  •  newtty = termios.tcgetattr(fd)
  •  newtty[3] = newtty[3] | termios.ICANON
  •  try:
  •   termios.tcsetattr(fd, termios.TCSANOW, newtty)
  •   tty.setraw(fd)
  •   tty.setcbreak(fd)
  •   chan.settimeout(0.0)
  •   while True:
  •    try:
  •     r, w, e = select.select([chan, stdin], [], [])
  •    except:
  •     # 解决SIGWINCH信号将休眠的select系统调用唤醒引发的系统中断,忽略中断重新调用解决。
  •     continue
  •    if chan in r:
  •     try:
  •      x = chan.recv(1024)
  •      if len(x) == 0:
  •       print 'rn*** EOFrn',
  •       break
  •      sys.stdout.write(x)
  •      sys.stdout.flush()
  •     except socket.timeout:
  •      pass
  •    if stdin in r:
  •     x = stdin.read(1)
  •     if len(x) == 0:
  •      break
  •     chan.send(x)
  •  finally:
  •   termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
  • 使用示例:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • #coding=utf8
  • import paramiko
  • import interactive
  •  
  •  
  • #记录日志
  • paramiko.util.log_to_file('/tmp/aaa')
  • #建立ssh连接
  • ssh=paramiko.SSHClient()
  • ssh.load_system_host_keys()
  • ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  • ssh.connect('192.168.1.11',port=22,username='hahaha',password='********',compress=True)
  •  
  • #建立交互式shell连接
  • channel=ssh.invoke_shell()
  • #建立交互式管道
  • interactive.interactive_shell(channel)
  • #关闭连接
  • channel.close()
  • ssh.close()
  • interactive.py代码中主要修复了几个问题:

    1、当读取键盘输入时,方向键会有问题,因为按一次方向键会产生3个字节数据,我的理解是按键一次会被select捕捉一次标准输入有变化,但是我每次只处理1个字节的数据,其他的数据会存放在输入缓冲区中,等待下次按键的时候一起发过去。这就导致了本来3个字节才能完整定义一个方向键的行为,但是我只发过去一个字节,所以终端并不知道我要干什么。所以没有变化,当下次触发按键,才会把上一次的信息完整发过去,看起来就是按一下方向键有延迟。多字节的粘贴也是一个原理。解决办法是将输入缓冲区置为0,这样就没有缓冲,有多少发过去多少,这样就不会有那种显示的延迟问题了。

    2、终端大小适应。paramiko.channel会创建一个pty(伪终端),有个默认的大小(width=80, height=24),所以登录过去会发现能显示的区域很小,并且是固定的。编辑vim的时候尤其痛苦。channel中有resize_pty方法,但是需要获取到当前终端的大小。经查找,当终端窗口发生变化时,系统会给前台进程组发送SIGWINCH信号,也就是当进程收到该信号时,获取一下当前size,然后再同步到pty中,那pty中的进程等于也感受到了窗口变化,也会收到SIGWINCH信号。

    3、读写‘慢'设备(包括pipe,终端设备,网络连接等)。读时,数据不存在,需要等待;写时,缓冲区满或其他原因,需要等待。ssh通道属于这一类的。本来进程因为网络没有通信,select调用为阻塞中的状态,但是当终端窗口大小变化,接收到SIGWINCH信号被唤醒。此时select会出现异常,触发系统中断(4, 'Interrupted system call'),但是这种情况只会出现一次,当重新调用select方法又会恢复正常。所以捕获到select异常后重新进行select可以解决该问题。

    以上这篇python获取交互式ssh shell的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持开心学习网。

    原文链接:https://blog.csdn.net/zlucifer/article/details/70858491

    标签:
    上一篇下一篇

    猜您喜欢

    热门推荐