Python | paramiko的概念及其使用

本文主要介绍 paramiko 的概念及其使用 1234
本文环境 Centos 7.9, Python 3.7, Paramiko 2.7.2

Last Updated: 2022 / 9 / 9



概念

paramiko 5 是用 python 语言写的一个模块,遵循 SSH2 协议 (底层使用 cryptography ),支持以加密和认证的方式,进行远程服务器的连接。

由于使用的是 python 这样的能够跨平台运行的语言,所以所有 python 支持的平台,如 Linux, Solaris, BSD, MacOS X, Windows等,paramiko 都可以支持,因此,如果需要使用 SSH 从一个平台连接到另外一个平台,进行一系列的操作时,paramiko 是最佳工具之一。

举个常见的例子,现有这样的需求:需要使用 windows 客户端,远程连接到 Linux 服务器,查看上面的日志状态,大家通常使用的方法会是:

  1. telnet ;
  2. PUTTY ;
  3. WinSCP ;
  4. XManager 等;

那现在如果需求又增加一条,要从服务器上下载文件,该怎么办?那常用的办法可能会是:

  1. Linux 上安装 FTP 并配置 ;
  2. Linux 上安装 Sambe 并配置 ;

大家会发现,常见的解决方法都会需要对远程服务器必要的配置,如果远程服务器只有一两台还好说,如果有N台,还需要逐台进行配置,或者需要使用代码进行以上操作时,上面的办法就不太方便了。

使用 paramiko 可以很好的解决以上问题。比起前面的方法,它仅需要在本地上安装相应的软件(python 以及 PyCrypto ),对远程服务器没有配置要求,对于连接多台服务器,进行复杂的连接操作特别有帮助,可以在 Python 代码中直接使用 SSH 协议对远程服务器执行操作,而不是通过 ssh 命令对远程服务器进行操作,比如连接远程服务器、执行命令和上传、下载文件的功能。。


安装

安装 paramiko 有两个先决条件,python 和另外一个名为 PyCrypto 的模块。


Linux

通常安装标准的 python 模块,只需要在模块的根目录下运行:

python setup.py build
python setup.py install

或者,

pip install xxx

paramikoPyCrypto 也不例外,唯一麻烦的就是安装 PyCrypto 时,需要 GCC 库编译,如果没有 GCC 库会报错,会导致 PyCrypto 以及 paramiko 无法安装。

如果是离线安装 6,需要先准备好所有相关的依赖包(bcryptcfficryptographypycparserPyNaClsix),这些依赖包都可以在 https://pypi.org/ 上搜到 6
离线安装顺序很重要,装下一个是基于上一个,大概顺序是:pycparse -> cffi (libffi-devel 是前提依赖 7891011) -> bcrypt -> six -> pynacl -> asnlcrypto -> cryptography (openssl-devel 是前提依赖 121314151617) -> paramiko 18

如果你装的时候包已经有更新的版本了,可以先找一台可以联网的电脑,记录下它从网上下载的文件记录,然后去找对应的包下载下来,拷到无法联网的电脑上随便什么顺序安装即可。

安装完成后可以在终端中输入 python -c "import paramiko", 如果没有报错则说明安装成功。


问题

  1. 如果出现未知异常,比如提示信息说 serialization.Encoding.Raw in kex_curve25519.py, 可能是由于依赖库之一的 cryptography 的版本问题 19

windows

以下以32 位的 windows XP 为例,说明 paramiko 的安装过程:

  1. 安装python,2.2 以上版本都可以,安装过程略,并假设安装目录是 c:\python ;
  2. 判断本地是否安装了 GCC,并在 PATH 变量可以找到。
    如果没有,可使用 windows 版的 GCC,即 MinGW,下载地址为 http://sourceforge.net/projects/mingw/,然后运行下载后的 exe 文件进行网络安装,假设目录为 C:\mingw,在 PATH 中加入 C:\mingw\bin,并在 c:\python\lib\distutils 下新建一个名称是 distutils.cfg 的文件,填入:
[build] 
compiler=mingw32
  1. 下载 PyCrypto ,地址是 https://www.dlitz.net/software/pycrypto/
    安装PyCrypto的步骤为,解压缩,在 dos 下进入解压缩的目录,运行
C:\python\python.exe setup.py build
C:\python\python.exe setup.py install

测试 paramiko, 运行 python.exe,在提示符下输入 Import paramiko。如果没有出现错误提示,说明 paramiko 安装成功


使用

单线程

SSH

连接
SSHClient
语法
client = paramiko.SSHClient.connect(
	hostname,
	port=SSH_PORT,
	username=None,
	password=None,
	pkey=None,
	key_filename=None,
	timeout=None,
	allow_agent=True,
	look_for_keys=True,
	compress=False,
	sock=None,
	gss_auth=False,
	gss_kex=False,
	gss_deleg_creds=True,
	gss_host=None,
	banner_timeout=None,
	auth_timeout=None,
	gss_trust_dns=True,
	passphrase=None,
	disabled_algorithms=None,
)
参数含义
hostname连接的目标主机。
port=SSH_PORT指定端口 20
username=None验证的用户名
password=None验证的用户密码
pkey=None私钥方式用于身份验证
key_filename=None一个文件名或文件列表,指定私钥文件
timeout=None可选的 tcp 连接超时时间
allow_agent=True是否允许连接到 ssh 代理,默认为 True 允许
look_for_keys=True是否在 ~/.ssh 中搜索私钥文件,默认为 True 允许
compress=False是否打开压缩

应对策略
ssh.set_missing_host_key_policy()

设置连接的远程主机没有本地主机密钥或 HostKeys 对象时的策略,目前支持三种:

  • AutoAddPolicy
    自动添加主机名及主机密钥到本地 HostKeys 对象,不依赖load_system_host_key 的配置。即新建立 ssh 连接时不需要再输入 yesno 进行确认;
  • WarningPolicy
    用于记录一个未知的主机密钥的 python 警告。并接受,功能上和 AutoAddPolicy 类似,但是会提示是新连接;
  • RejectPolicy
    自动拒绝未知的主机名和密钥,依赖 load_system_host_key 的配置。此为默认选项;

执行命令
语法
exec_command(
	command,
    bufsize=-1,
    timeout=None,
    get_pty=False,
    environment=None,
)
参数含义
command可以任意 linux 支持的命令,如一些常用的命令:

cp - 复制代码
df - 查看磁盘使用情况
uptime - 显示系统运行时间信息
cat - 显示某文件内容
mv/cp/mkdir/rmdir - 对文件或目录进行操作
/sbin/service/ xxxservice start/stop/restart - 启动、停止、重启某服务
netstat -ntl \grep 8080 - 查看 8080 端口的使用情况
nc -zv localhost - 查看所有端口的使用情况
find / -name XXX - 查找某文件

SSH服务端的实现

实现 SSH 服务端必须继承 ServerInterface,并实现里面相应的方法。具体代码如下:

import socket
import sys
import threading
import paramiko

host_key = paramiko.RSAKey(filename='private_key.key')

class Server(paramiko.ServerInterface):
  def __init__(self):
  #执行start_server()方法首先会触发Event,如果返回成功,is_active返回True
    self.event = threading.Event()

  #当is_active返回True,进入到认证阶段
  def check_auth_password(self, username, password):
    if (username == 'root') and (password == '123456'):
      return paramiko.AUTH_SUCCESSFUL
    return paramiko.AUTH_FAILED

  #当认证成功,client会请求打开一个Channel
  def check_channel_request(self, kind, chanid):
    if kind == 'session':
      return paramiko.OPEN_SUCCEEDED
#命令行接收ip与port
server = sys.argv[1]
ssh_port = int(sys.argv[2])

#建立socket
try:
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #TCP socket
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  sock.bind((server, ssh_port))  
  sock.listen(100)  
  print '[+] Listening for connection ...'
  client, addr = sock.accept()
except Exception, e:
  print '[-] Listen failed: ' + str(e)
  sys.exit(1)
print '[+] Got a connection!'

try:
  #用sock.accept()返回的socket实例化Transport
  bhSession = paramiko.Transport(client)
  #添加一个RSA密钥加密会话
  bhSession.add_server_key(host_key)
  server = Server()
  try:
  #启动SSH服务端
    bhSession.start_server(server=server)
  except paramiko.SSHException, x:
    print '[-] SSH negotiation failed'
  chan = bhSession.accept(20) 
  print '[+] Authenticated!'
  print chan.recv(1024)
  chan.send("Welcome to my ssh")
  while True:
    try:
      command = raw_input("Enter command:").strip("\n") 
      if command != 'exit':
        chan.send(command)
        print chan.recv(1024) + '\n'
      else:
        chan.send('exit')
        print 'exiting'
        bhSession.close()
        raise Exception('exit')
    except KeyboardInterrupt:
      bhSession.close()
except Exception, e:
  print '[-] Caught exception: ' + str(e)
  try:
    bhSession.close()
  except:
    pass
  sys.exit(1)

示例

参考此处 22122


基于用户名和密码登录
ssh = paramiko.SSHClient()				
# 实例化SSHClient,新建client对象

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 自动添加策略,保存服务器的主机名和密钥信息进入host_allow列表,此方法必须放在connect方法的前面。
# 如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接

ssh.connect(hostname='192.168.1.1', port=22, username='root', password='123456')
# 调用connect()方法连接SSH服务端,以用户名和密码进行认证

stdin, stdout, stderr = client.exec_command('df -h ') 
# stdout 为正确输出,stderr为错误输出,同时是有1个变量有值
# 打开一个Channel并执行命令

print(stdout.read().decode('utf-8'))
# print(stdout.readlines())
# 打印执行结果

client.close()
# 关闭SSHClient

stdin 无法被 read(),它是 input, 而非 output,否则将会报出 "File is not open for reading" 23


基于公钥密钥登录

以上需要确保被访问的服务器对应用户 .ssh 目录下有authorized_keys 文件,也就是将服务器上生成的公钥文件保存为authorized_keys。并将私钥文件作为 paramiko 的登陆密钥。

pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
# 指定本地的RSA私钥文件,如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数

ssh = paramiko.SSHClient()
# 建立连接
ssh.connect(hostname='192.168.2.129',
      port=22,
      username='super',
      pkey=pkey)

stdin, stdout, stderr = ssh.exec_command('df -hl')
print(stdout.read().decode())
# 执行命令, 结果放到stdout中,如果有错误将放到stderr中

ssh.close()
# 关闭连接

SFTP

参考此处 321

连接
Transport

SSHClient 是传统的连接服务器、执行命令、关闭的一个操作,有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,SSHClient 则无法实现,可以通过如下方式来操作:


语法
t = paramiko.Transport(('192.168.2.129', 22))
# 实例化一个trans对象# 实例化一个transport对象

t.connect(username='super', password='super')
# 建立连接

sftp = paramiko.SFTPClient.from_transport(t)
# 实例化一个 sftp对象,指定连接的通道

上传 / 下载
文件

传输单个文件的方法为 getput,但每次只能传输一个文件,而不是整个目录。

比如,传输单个文件的示例如下:

t = paramiko.Transport(('192.168.2.129', 22))
# 实例化一个trans对象# 实例化一个transport对象

t.connect(username='super', password='super')
# 建立连接

sftp = paramiko.SFTPClient.from_transport(t)
# 实例化一个 sftp对象,指定连接的通道

sftp.put(localpath='/tmp/11.txt', remotepath='/Desktop/22.txt')
# 发送文件
# 将本地tmp文件夹下的22.txt put到远端Desktop文件夹并保存为22.txt

sftp.get(remotepath='Desktop/22.txt', localpath='tmp/11.txt')
# 下载文件
# 将远端的Desktop文件夹下的22.txt get到本地tmp文件夹并保存为11.txt

t.close()
# 关闭连接

上面的 remotepathlocalpath 一定是文件的路径,不能是目录路径。


文件夹

如何传输整个目录?242526

有两种思路:

  1. 如果是要 get 则采用 connect 方法连接到linux主机,然后通过 send 方法执行 tar 命令将需要传输的整个目录打包,再传输打包后的文件即可,如果是 put 则需在本地打包。
  • 缺点

在远端或者本地进行打包或者解压,并且打包会占用临时存储空间,如果是远端打包还需先SSH 连接 linux主机。

  • 优点

不用做目录扫描处理。

  1. 遍历需要 getput 的目录及其子目录,然后依次传输每一个文件。
  • 优点

不需要 SSH 登陆和打包解压。

  • 缺点

需要做目录扫描,但是目录扫描是很简单的,因此我们采用这种方法。

由于要扫描目录,因此先定义一个方法用来对指定目录进行扫描,找出该目录及所有子目录中的所有文件。

那么,怎么扫描目录呢?使用 pythonos 库的方法吗?肯定是不行的,因为 pythonos 库的方法都是对本地目录或文件的操作,它是无法操作远程linux 主机上的文件和目录的。
但是 paramikoSFTP 接口提供了操作远端 linux 主机上的文件和目录的方法,只要建立了与远端的SFTP 连接后,就可以执行文件和目录操作。

import paramiko
import stat

t = paramiko.Transport(('192.168.2.129', 22))
t.connect(username='YourUsername', password='YourPassword')
sftp = paramiko.SFTPClient.from_transport(t)

remote_dir = '/Desktop/Downloads/paho-mqtt-1.5.1'

def get_file_list_in_remote_dir(sftp, remote_dir):
    file_list = list()

    if remote_dir[-1] == '/':
        remote_dir = remote_dir[0:-1]

    files = sftp.listdir_attr(remote_dir)
    for file in files:
        filename = remote_dir + '/' + file.filename
        if stat.S_ISDIR(file.st_mode):
            file_list.extend(get_file_list_in_remote_dir(sftp, filename))
        if stat.S_ISREG(file.st_mode):
            file_list.append(filename)

    return file_list
    
file_list = get_file_list_in_remote_dir(sftp=sftp, remote_dir=remote_dir)

在上面的方法中,sftp 表示已经建立的 sftp 连接,remote_dir 是要扫描的远端目录。

在扫描目录的时候,使用的 sftp.listdir_attr 方法会列出指定目录下的所有文件或目录,并且还会列出其属性,比如st_size, st_uid , st_gid , st_mode , st_atime , st_mtime。这些属性与 linux 中的 stat 函数返回的属性类似,我们就是根据其中的 st_mode 属性来判断是一个目录还是文件,并且处理 st_mode 的方法(位于 stat 模块中)也是与 linux 中定义的宏一致的。

获取到指定目录下的所有文件之后,传输就比较简单了,依次遍历 get 即可:

import paramiko
import stat
import os

t = paramiko.Transport(('192.168.2.129', 22))
t.connect(username='YourUsername', password='YourPassword')
sftp = paramiko.SFTPClient.from_transport(t)

remote_dir = '/Desktop/Downloads/paho-mqtt-1.5.1'
local_dir = 'D:\\Test'

def get_file_list_in_remote_dir(sftp, remote_dir):
    file_list = list()

    if remote_dir[-1] == '/':
        remote_dir = remote_dir[0:-1]

    files = sftp.listdir_attr(remote_dir)
    for file in files:
        filename = remote_dir + '/' + file.filename
        if stat.S_ISDIR(file.st_mode):
            file_list.extend(get_file_list_in_remote_dir(sftp, filename))
        if stat.S_ISREG(file.st_mode):
            file_list.append(filename)

    return file_list

def get_dir(sftp, remote_dir, local_dir):
    file_list = get_file_list_in_remote_dir(sftp, remote_dir)
    for file in file_list:
        file_subdir = os.path.dirname(file).split(remote_dir)[-1]
        file_subdir = file_subdir.replace('/', '\\')
        file_name = os.path.basename(file)
        local_filedir = local_dir + file_subdir
        local_filename = local_filedir + '\\' + file_name
        if not os.path.exists(local_filedir):
            os.makedirs(local_filedir)
        sftp.get(file, local_filename)

get_dir(sftp, remote_dir, local_dir)

上面方法将 remote_dir 目录中的所有文件都按与远端一致的目录结构 get 到了本地 local_dir 目录中。


下面再来看看 put,其实与 get 几乎一样,现在扫描本地目录,然后依次遍历文件并 put 到远端 27

由于是对本地目录做扫描,因此不需要调用 SFTP 中的文件目录处理接口了,直接使用 pythonos 库即可,代码如下:

import paramiko
import stat
import os

t = paramiko.Transport(('192.168.2.129', 22))
t.connect(username='YourUsername', password='YourPassword')
sftp = paramiko.SFTPClient.from_transport(t)

local_dir = 'D:\\Test'
remote_dir = '/Desktop/Downloads/paho-mqtt-1.5.1'

def get_file_list_in_local_dir(sftp, local_dir):
    file_list = list()

    files = os.listdir(local_dir)
    for file in files:
        filename = os.path.join(local_dir, file)
        if os.path.isdir(filename):
            file_list.extend(get_file_list_in_local_dir(sftp, filename))
        else:
            file_list.append(filename)

    return file_list

def put_dir(sftp, local_dir, remote_dir):
    file_list = get_file_list_in_local_dir(sftp, local_dir)
    for file in file_list:
        file_subdir = os.path.dirname(file).split(local_dir)[-1]
        file_subdir = file_subdir.replace('\\', '/')
        file_name = os.path.basename(file)
        remote_filedir = remote_dir + file_subdir
        if remote_filedir.endswith('/'):
            remote_filedir = remote_filedir[0:-1]
        remote_filename = remote_filedir + '/' + file_name
        try:
            sftp.stat(remote_filedir)
        except:
            sftp.mkdir(remote_filedir)
        sftp.put(file, remote_filename, confirm=True)

put_dir(sftp, local_dir=local_dir, remote_dir=remote_dir)

执行命令
语法
exec_command(
	command,
    bufsize=-1,
    timeout=None,
    get_pty=False,
    environment=None,
)
参数含义
command可以任意 linux 支持的命令,如一些常用的命令:

cp - 复制代码
df - 查看磁盘使用情况
uptime - 显示系统运行时间信息
cat - 显示某文件内容
mv/cp/mkdir/rmdir - 对文件或目录进行操作
/sbin/service/ xxxservice start/stop/restart - 启动、停止、重启某服务
netstat -ntl \grep 8080 - 查看 8080 端口的使用情况
nc -zv localhost - 查看所有端口的使用情况
find / -name XXX - 查找某文件

Xshell-like功能

注意,在 windows 中,sys.stdin 不是一个 socket 或者 file-like 对象,而是一个 PseudoOutputFile 对象,不能被 select 处理。所以上面的脚本不能在 windows 中运行,只能用于 linux


输入命令后立马返回结果
import paramiko
import os
import select
import sys

# 建立一个socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 启动一个客户端
trans.start_client()

# 如果使用rsa密钥登录的话
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 如果使用用户名和密码登录
trans.auth_password(username='super', password='super')
# 打开一个通道
channel = trans.open_session()
# 获取终端
channel.get_pty()
# 激活终端,这样就可以登录到终端了,就和我们用类似于xshell登录系统一样
channel.invoke_shell()
# 下面就可以执行你所有的操作,用select实现
# 对输入终端sys.stdin和 通道进行监控,
# 当用户在终端输入命令后,将命令交给channel通道,这个时候sys.stdin就发生变化,select就可以感知
# channel的发送命令、获取结果过程其实就是一个socket的发送和接受信息的过程
while True:
  readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
  # 如果是用户输入命令了,sys.stdin发生变化
  if sys.stdin in readlist:
    # 获取输入的内容
    input_cmd = sys.stdin.read(1)
    # 将命令发送给服务器
    channel.sendall(input_cmd)

  # 服务器返回了结果,channel通道接受到结果,发生变化 select感知到
  if channel in readlist:
    # 获取结果
    result = channel.recv(1024)
    # 断开连接后退出
    if len(result) == 0:
      print("\r\n**** EOF **** \r\n")
      break
    # 输出到屏幕
    sys.stdout.write(result.decode())
    sys.stdout.flush()

# 关闭通道
channel.close()
# 关闭链接
trans.close()

tab自动补全
import paramiko
import os
import select
import sys
import tty
import termios

'''
实现一个xshell登录系统的效果,登录到系统就不断输入命令同时返回结果
支持自动补全,直接调用服务器终端

'''
# 建立一个socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 启动一个客户端
trans.start_client()

# 如果使用rsa密钥登录的话
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 如果使用用户名和密码登录
trans.auth_password(username='super', password='super')
# 打开一个通道
channel = trans.open_session()
# 获取终端
channel.get_pty()
# 激活终端,这样就可以登录到终端了,就和我们用类似于xshell登录系统一样
channel.invoke_shell()

# 获取原操作终端属性
oldtty = termios.tcgetattr(sys.stdin)
try:
  # 将现在的操作终端属性设置为服务器上的原生终端属性,可以支持tab了
  tty.setraw(sys.stdin)
  channel.settimeout(0)

  while True:
    readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
    # 如果是用户输入命令了,sys.stdin发生变化
    if sys.stdin in readlist:
      # 获取输入的内容,输入一个字符发送1个字符
      input_cmd = sys.stdin.read(1)
      # 将命令发送给服务器
      channel.sendall(input_cmd)

    # 服务器返回了结果,channel通道接受到结果,发生变化 select感知到
    if channel in readlist:
      # 获取结果
      result = channel.recv(1024)
      # 断开连接后退出
      if len(result) == 0:
        print("\r\n**** EOF **** \r\n")
        break
      # 输出到屏幕
      sys.stdout.write(result.decode())
      sys.stdout.flush()
finally:
  # 执行完后将现在的终端属性恢复为原操作终端属性
  termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

# 关闭通道
channel.close()
# 关闭链接
trans.close()

示例
基于用户名和密码登录
t = paramiko.Transport(('192.168.2.129', 22))
# 实例化一个transport对象

t.connect(username='super', password='super')

ssh = paramiko.SSHClient()
ssh._transport = t
# 将sshclient的对象ssh的transport指定为以上的t

stdin, stdout, stderr = ssh.exec_command('df -hl')
print(stdout.read().decode())
# 执行命令,和传统方法一样

t.close()
# 关闭连接

基于公钥密钥登录
pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
# 指定本地的RSA私钥文件,如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数

# 建立连接
t = paramiko.Transport(('192.168.2.129', 22))
t.connect(username='super', pkey=pkey)

ssh = paramiko.SSHClient()
ssh._transport = t
# 将sshclient的对象的transport指定为以上的trans

stdin, stdout, stderr = ssh.exec_command('df -hl')
print(stdout.read().decode())
# 执行命令,和传统方法一样

t.close()
# 关闭连接
t = paramiko.Transport((“主机”,”端口”))
# 实例化一个传输对象

t.connect(username = “用户名”, password = “口令”)
# 建立连接。如果连接远程主机需要提供密钥,上面第二行代码可改成:
# t.connect(username = “用户名”, password = “口令”, hostkey=”密钥”)

sftp = paramiko.SFTPClient.from_transport(transport) 
# 将实例Transport作为参数传入SFTPClient中

sftp.put(r'E:\Users\Desktop\test.txt','/opt/test1.txt') 
# 将本地桌面test.txt文件上传至虚拟机opt目录下

sftp.get('/opt/test2.txt',r'E:\Users\Desktop\test2.txt') 
# 将虚拟机opt目录下test2.txt下载到本地桌面
    
t.close() 
#关闭连接

f’ ‘:格式化
r’ ‘:去除转义
b’ ‘:指定为字节类型
u’ ':指定为Unicode编码


多线程

SSH

#!/usr/bin/env python3
import sys
import paramiko
import threading
import os

def remote_comm(host, pwd, command):
    '''
    创建函数实现远程连接主机、服务器密码以及在远程主机上执行的命令的功能
    :param host:
    :param pwd:
    :param command:
    :return:
    '''
    ssh = paramiko.SSHClient()
    # 创建用于连接ssh服务器的实例
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 设置自动添加主机密钥

    try:
       ssh.connect(hostname=host, username='root', password=pwd)
       # 连接ssh服务器,添加连接的主机、用户名、密码填好,捕获异常,有异常则跳出函数
    except:
       return

    stdin, stdout, stderr = ssh.exec_command(command)
    # 在ssh服务器上执行指定命令,返回3项类文件对象,分别是,输入、输出、错误
    out = stdout.read()                             # 读取输出
    error = stderr.read()                           # 读取错误
    if out:                                         # 如果有输出
        print('[%s] OUT:\n%s' % (host, out.decode('utf8')))
        # 打印主机输出内容
    if error:                                       # 如果有错误
        print('[%s] ERROR:\n%s' % (host, error.decode('utf8')))
        # 打印主机错误信息
    ssh.close()                                     # 程序结束

if __name__ == '__main__':
    if len(sys.argv) != 3:
    # 设定sys.argv长度,确保remote_comm函数中参数数量
        print('Usage: %s ipaddr_file "command"' % sys.argv[0])
        exit(1)
    if not os.path.isfile(sys.argv[1]):
    # 判断命令行上输入如果不是文件,确保输入的是文件
        print('No such file:', sys.argv[1])
        exit(2)
    fname = sys.argv[1]
    # fname为存储远程主机ip的文件,用sys.argv方法,可以在执行脚本时再输入文件名,更为灵活
    command = sys.argv[2]
    # command为在远程主机上执行的命令,用sys.argv方法,可以在执行脚本时再输入相应命令,command为remote_comm函数第三个参数
    # 通过getpass输入远程服务器密码,pwd为remote_comm函数第二个参数
    # pwd = getpass.getpass()
    pwd='Taren1.bgsn'

    with open(fname) as fobj:
    # 打开存有远程主机ip的文件
        ips = [line.strip() for line in fobj]
        # 将遍历文件将ip以列表形式存入ips,line.strip()可以去掉每行ip后
        for ip in ips:
        # 循环遍历列表,获取ip地址,ip为remote_comm函数第一个参数
        # 将读取到的ip地址作为remote_comm函数实际参数传递给函数,ips中有几个ip地址循环几次
            t = threading.Thread(target=remote_comm, args=(ip, pwd, command))
            # 创建多线程
            t.start()
            # 启用多线程

详情需参考此处 2


参考链接


  1. python使用paramiko实现ssh的功能详解 ↩︎

  2. paramiko模块怎么在Python项目中使用 ↩︎ ↩︎ ↩︎

  3. paramiko的安装与使用 ↩︎ ↩︎

  4. paramiko 远程连接ssh ↩︎

  5. paramiko ↩︎

  6. windows下python3.7离线安装paramiko库全文件,亲测有用,太折腾人了 ↩︎ ↩︎

  7. Installing cffi fails with c/_cffi_backend.c:15:17: fatal error: ffi.h: No such file or directory ↩︎

  8. linux系统中离线安装python3.7过程记录 ↩︎

  9. [小技巧] Linux 里快速安装缺少的库 ↩︎

  10. Libffi-devel Download for Linux (eopkg, rpm, xbps) ↩︎

  11. 关于在centos下安装python3.7.0以上版本时报错ModuleNotFoundError: No module named ‘_ctypes‘的解决办法 ↩︎

  12. How to fix: fatal error: openssl/opensslv.h: No such file or directory in RedHat 7 ↩︎

  13. Linux Ubuntu openssl离线源码安装、升级版本 ↩︎

  14. Frequently asked questions ↩︎

  15. Linux中的 openssl/opensslv.h找不到的问题解决 ↩︎

  16. Openssl-devel Download for Linux (eopkg, pkg, rpm, xbps) ↩︎

  17. Welcome to OpenSSL! ↩︎

  18. Paramiko Docs ↩︎

  19. paramiko.SSHClient.connect raises unknown exception ↩︎

  20. IP地址、MAC地址以及端口号 ↩︎

  21. paramiko模块使用方法(十二) ↩︎ ↩︎

  22. ^paramiko连接方式 ↩︎

  23. Can’t get stderr with Paramiko ↩︎

  24. 使用paramiko的SFTP get或put整个目录 ↩︎

  25. Python3-paramiko-SFTP上传文件夹到多台远程服务器 ↩︎

  26. python之paramiko文件夹远程上传 ↩︎

  27. paramiko怎么判断远程目录是否存在? ↩︎

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值