Python批量管理主机(paramiko、fabric与pexpect)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
日志服务 SLS,月写入数据量 50GB 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

paramiko

paramiko模块是基于Python实现的SSH远程安全连接,用于SSH远程执行命令、文件传输等功能。


默认Python没有,需要手动安装:pip install paramiko


如安装失败,可以尝试yum安装:yum install python-paramiko



基于账号密码的形式,执行命令或上传下载文件

import paramiko
 
# 基于账号密码执行命令
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的机器
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname="192.168.80.20", port=22, username="root", password="test@2015")
# 执行命令
stdin, stdout, stderr = ssh.exec_command("free -m")
# 获取命令结果
res, err = stdout.read(), stderr.read()
result = res if res else err
print(result.decode())
# 关闭连接
ssh.close()
 
# 基于账号密码的上传下载
transport = paramiko.Transport(("192.168.80.20", 22))
transport.connect(username="root", password="test@2015")
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py上传至服务器的/tmp/test.py
sftp.put("高级FTP.png", "/root/高级FTP.png")
# 将remove_path下载到本地local_path
sftp.get("remove_path", "local_path")
transport.close()
 
# SSHClient 封装 Transport
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', password='123')
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
print(stdout.read().decode())
transport.close()






基于密钥的形式,执行命令或上传下载文件




import paramiko
 
# 基于密钥执行命令
private_key = paramiko.RSAKey.from_private_key_file("id_rsa")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname="192.168.80.24", port=22, username="root", pkey=private_key)
stdin, stdout, stderr = ssh.exec_command("ifconfig")
res, err = stdout.read(), stderr.read()
result = res if res else err
print(result.decode())
ssh.close()
 
# 基于密钥的上传下载
private_key = paramiko.RSAKey.from_private_key_file("id_rsa")
transport = paramiko.Transport(("192.168.80.24", 22))
transport.connect(username="root", pkey=private_key)
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.get("test.txt", "1")
transport.close()
 
# SSHClient 封装 Transport
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
transport.close()


SSH密码认证远程执行命令



#!/usr/bin/python

# -*- coding: utf-8 -*-

import paramiko

import sys

hostname = '192.168.1.215'

port = 22

username = 'root'

password = '123456'

client = paramiko.SSHClient()  # 绑定实例

client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect(hostname, port, username, password, timeout=5)

stdin, stdout, stderr = client.exec_command('df -h')   # 执行bash命令

result = stdout.read()

error = stderr.read()

# 判断stderr输出是否为空,为空则打印执行结果,不为空打印报错信息

if not error:

   print result

else:

   print error

client.close()


私钥认证远程执行命令


#!/usr/bin/python

# -*- coding: utf-8 -*-

import paramiko

import sys

hostname = '192.168.1.215'

port = 22

username = 'root'

key_file = '/root/.ssh/id_rsa'

cmd = " ".join(sys.argv[1:])

def ssh_conn(command):

    client = paramiko.SSHClient()

    key = paramiko.RSAKey.from_private_key_file(key_file)

    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    client.connect(hostname, port, username, pkey=key)

    stdin, stdout, stderr = client.exec_command(command)  # 标准输入,标准输出,错误输出

    result = stdout.read()

    error = stderr.read()

    if not error:

        print result

    else:

        print error

    client.close()

if __name__ == "__main__":

    ssh_conn(cmd)


上传文件到远程服务器


#!/usr/bin/python

# -*- coding: utf-8 -*-

import os, sys

import paramiko

hostname = '192.168.1.215'

port = 22

username = 'root'

password = '123456'

local_path = '/root/test.txt'

remote_path = '/opt/test.txt'

if not os.path.isfile(local_path):

    print local_path + " file not exist!"

    sys.exit(1)

try:

    s = paramiko.Transport((hostname, port))

    s.connect(username = username, password=password)

except Exception as e:

    print e

    sys.exit(1)

sftp = paramiko.SFTPClient.from_transport(s)


# 使用put()方法把本地文件上传到远程服务器


sftp.put(local_path, remote_path)  

     

# 简单测试是否上传成功

try:

    # 如果远程主机有这个文件则返回一个对象,否则抛出异常               

    sftp.file(remote_path) 

    print "上传成功."

except IOError:

    print "上传失败!"

finally:

    s.close()


从远程服务器下载文件


#!/usr/bin/python

# -*- coding: utf-8 -*-

import os, sys

import paramiko

hostname = '192.168.1.215'

port = 22

username = 'root'

password = '123456'

local_path = '/root/test.txt'

remote_path = '/opt/test.txt'

try:

    s = paramiko.Transport((hostname, port))

    s.connect(username=username, password=password)

    sftp = paramiko.SFTPClient.from_transport(s)

except Exception as e:

    print e

    sys.exit(1)

try:

    # 判断远程服务器是否有这个文件

    sftp.file(remote_path)

    # 使用get()方法从远程服务器拉去文件

    sftp.get(remote_path, local_path)       

except IOError as e:

    print remote_path + "remote file not exist!"

    sys.exit(1)

finally:

    s.close()

# 测试是否下载成功

if os.path.isfile(local_path):

    print "下载成功."

else:

    print "下载失败!"



上传目录到远程服务器

paramiko模块并没有实现直接上传目录的类,已经知道了如何上传文件,再写一个上传目录的代码就简单了,利用os库的os.walk()方法遍历目录,再一个个上传:

#!/usr/bin/python

# -*- coding: utf-8 -*-

import os, sys

import paramiko

hostname = '192.168.1.215'

port = 22

username = 'root'

password = '123456'

local_path = '/root/abc'

remote_path = '/opt/abc'

# 去除路径后面正斜杠

if local_path[-1] == '/':

    local_path = local_path[0:-1]

if remote_path[-1] == '/':

    remote_path = remote_path[0:-1]

file_list = []

if os.path.isdir(local_path):

    for root, dirs, files in os.walk(local_path):

        for file in files:

            # 获取文件绝对路径

            file_path = os.path.join(root, file) 

            file_list.append(file_path)

else:

    print path + "Directory not exist!"

    sys.exit(1)

try:

    s = paramiko.Transport((hostname, port))

    s.connect(username=username, password=password)

    sftp = paramiko.SFTPClient.from_transport(s)

except Exception as e:

    print e

for local_file in file_list:

    # 替换目标目录

    remote_file = local_file.replace(local_path, remote_path)

    remote_dir = os.path.dirname(remote_file)

    # 如果远程服务器没目标目录则创建

    try:

        sftp.stat(remote_dir)

    except IOError:

        sftp.mkdir(remote_dir)

    print "%s -> %s" % (local_file, remote_file)

    sftp.put(local_file, remote_file)

s.close()


传输文件


#!/usr/bin/python

#-*- coding:UTF-8 -*-


try:

    import os,sys

    import re

    import datetime

    import shutil

    import commands


    from optparse import OptionParser

except ImportError,e:

    print "Error:",e

    sys.exit()


def Get_And_ScpLogfile(host,list,tmpdir,yestoday):

    logdir = '/log/'

    desdir = '/home/haoren/logdir/'

    global dirs;dirs = []

    for ip in list:

        ident = ip.split('.')[3]

        ldir = '%s%s_%s' % (tmpdir,yestoday,ident)

        dirs.append(ldir) 

        if not os.path.exists(ldir):

                os.makedirs(ldir)


        s,o = commands.getstatusoutput('ssh %s /bin/ls -ld %s%s ' % (ip,logdir,yestoday))

        print "********************************************%s**************************************************" % ip

        print "BEGIN:"

        if s == 0:

            os.system('/usr/bin/scp -r %s:%s%s/* %s' % (ip,logdir,yestoday,ldir)) 

        else:

            os.system('/usr/bin/scp -r %s:%s*%s* %s' % (ip,logdir,yestoday,ldir)) 



    for ip in host:

         print "**************************************SCP_To_%s**************************************************" % ip

         print "BEGIN:"

         os.system('/usr/bin/scp -r %s%s* %s:%s' % (tmpdir,yestoday,ip,desdir))     


def CleanLogFile():

    for d in dirs:

        shutil.rmtree(d,True)


def main():

    ####option####

    parser = OptionParser()

    parser.add_option("-t","--time",action="store",dest="filedate",help="appoint the logfile time,use like this '-t 130825'")

    (options,args) = parser.parse_args()


    ####option####

    

    if options.filedate:

        filedate = options.filedate

    else:

        filedate = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%y%m%d')


    iplist = []

    host = []

    tmpdir = '/home/haoren/tempdir/'

    f = open('/home/haoren/tools/log_config.ini','r')

    for line in f:

        line = line.strip()

        if re.search(r'^[0-9]{1,2}=.*',line):

            iplist.append(line.split('=')[1])

        elif re.search(r'^host=.*',line):

            #host = line.split('=')[1]

            host.append(line.split('=')[1])

        elif re.search(r'^host2=.*',line):

        #    host2 = line.split('=')[1]

            host.append(line.split('=')[1])

    f.close()

    Get_And_ScpLogfile(host,iplist,tmpdir,filedate) 

    CleanLogFile()


if __name__ == "__main__":

    main()


shutil 用来处理 文件 文件夹 压缩包 的模块


import shutil

  

# 拷贝文件内容

shutil.copyfileobj(open('old.xml', 'r'), open('new.xml', 'w'))

  

# 拷贝文件

shutil.copyfile('f1.log', 'f2.log')

  

# 拷贝权限

shutil.copymode('f1.log', 'f2.log')

  

# 拷贝文件状态信息

shutil.copystat('f1.log', 'f2.log')

  

# 拷贝文件和权限

shutil.copy('f1.log', 'f2.log')

  

# 递归地拷贝文件夹

shutil.copytree('folder1', 'folder2',

ignore=shutil.ignore_patterns('*.pyc', '*.txt'))

  

# 递归地删除文件

shutil.rmtree('folder2')

  

# 递归地移动重命名文件

shutil.move('folder2', 'folder3')

  

# 打包文件

ret = shutil.make_archive(r'C:\GitHub\Python\day7\shutil\www', 'gztar',

root_dir=r'C:\GitHub\Python\day7\shutil\folder1') 



zipfile tarfile


import zipfile

  

# 压缩

z = zipfile.ZipFile('z.zip', 'w')

z.write('xo.xml')

z.write('xxxoo.xml')

z.close()

  

# 解压

z = zipfile.ZipFile('z.zip', 'r')

for item in z.namelist():

    print(item)

# z.extractall()

z.extract('xo.xml')

  

import tarfile

  

# 压缩

tar = tarfile.open('z.tar', 'w')

tar.add('xo.xml', arcname='bbs2.log')

tar.add('xxxoo.xml', arcname='cmdb.log')

tar.close()

  

# 解压

tar = tarfile.open('z.tar', 'r')

# for item in tar.getmembers():

#     print(item, type(item))

obj = tar.getmember('cmdb.log')  # 和zipfile不同的是 再解压特定文件前要先获取文件特殊对象值

tar.extract(obj)

tar.close()




和处理shell相关的命令


import subprocess

  

# 返回命令执行结果

# result = subprocess.call('ls -l', shell=True)

# result = subprocess.call(['ls', '-l'], shell=False)

# print(result)

  

# subprocess.check_call(["ls", "-l"])

# subprocess.check_call("exit 1", shell=True)

  

# 好像没Python废弃了

subprocess.check_output(["echo", "Hello World!"], shell=False)

subprocess.check_output("exit 1", shell=True)

  

# 2、执行复杂的系统相关命令

  

# 1)切换目录再执行命令

obj = subprocess.Popen("mkdir t3", shell=True, cwd='/home/dev',)

  

# 2)有多行且复杂的命令使用三个接口

# obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, 

stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

# obj.stdin.write("print(1)\n")  # 传命令接口

# obj.stdin.write("print(2)")

# obj.stdin.close()

# cmd_out = obj.stdout.read()  # 读接口

# obj.stdout.close()

# cmd_error = obj.stderr.read()  # 读错误接口

# obj.stderr.close()

# print(cmd_out)

# print(cmd_error)

  

# 3)一次读输出

# obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, 

stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

# obj.stdin.write("print(1)\n")

# obj.stdin.write("print(2)")

#

# out_error_list = obj.communicate()

# print(out_error_list)

  

# 4)简单写法

# obj = subprocess.Popen(["python"], stdin=subprocess.PIPE,

stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

# out_error_list = obj.communicate('print("hello")')

# print(out_error_list) 





xml 是实现不同语言和程序之间进行数据交换的协议



from xml.etree import ElementTree as ET

  

# xml有两个常见格式

# 1)直接读取字符串格式的xml

str_xml = open('xo.xml', 'r').read()

root = ET.XML(str_xml)  # 这里没有建立 xml tree 所以不能直接将内存中的xml写回文件

  

# 2)读取xml格式文件

# tree = ET.parse('xo.xml')  # 首先建立了一个 xml tree 对象

# root = tree.getroot()

# print(root)  # 获取根节点

# print(root.tag)  # 取根节点名

# print(root.attrib)  # 获取节点属性

  

# 3) 遍历多层xml

for child in root:

    print(child.tag, child.attrib)

    for child_second in child:

        print(child_second.tag, child_second.text)  # child_second.text 节点内容

  

# 4) 遍历指定的节点

for node in root.iter('year'):

    print(node.tag, node.text)

  

# 5) 修改节点内容

for node in root.iter('year'):

    new_year = int(node.text) + 1

    node.text = str(new_year)

  

    node.set('name', 'london')

    node.set('age', '18')

  

    del node.attrib['age']

  

tree = ET.ElementTree(root)

tree.write('new_xo.xml', encoding='utf-8')

   

# 6、删除节点

str_xml = open('xo.xml', 'r').read()

root = ET.XML(str_xml)

for country in root.findall('country'):

    rank = int(country.find('rank').text)

    if rank > 50:

        root.remove(country)

  

tree = ET.ElementTree(root)

tree.write('new_xoo.xml', encoding='utf-8')

  

# 7、创建 xml 文档

  

from xml.dom import minidom

  

  

def prettify(elem):

    """将节点转换成字符串,并添加缩进。

    """

    rough_string = ET.tostring(elem, 'utf-8')

    reparsed = minidom.parseString(rough_string)

    return reparsed.toprettyxml(indent="\t")

  

# 创建根节点

root = ET.Element("famliy")

  

# 创建大儿子

# son1 = ET.Element('son', {'name': '儿1'})

son1 = root.makeelement('son', {'name': '儿1'})

# 创建小儿子

# son2 = ET.Element('son', {"name": '儿2'})

son2 = root.makeelement('son', {"name": '儿2'})

  

# 在大儿子中创建两个孙子

# grandson1 = ET.Element('grandson', {'name': '儿11'})

grandson1 = son1.makeelement('grandson', {'name': '儿11'})

# grandson2 = ET.Element('grandson', {'name': '儿12'})

grandson2 = son1.makeelement('grandson', {'name': '儿12'})

  

son1.append(grandson1)

son1.append(grandson2)

  

  

# 把儿子添加到根节点中

root.append(son1)

root.append(son1)

  

raw_str = prettify(root)  # 自动添加缩进

  

f = open("xxxoo.xml", 'w', encoding='utf-8')

f.write(raw_str)

f.close()




configparser 用于处理特定格式的文件 实质上是通过open来操作文件

源文件特定格式


[section1]

k1 = 123

k2 = v2

  

[section2]

k1 = 456

k2 = v2

k3 = v3


常见操作

import configparser

  

# 1、读取文件 读取节点

config = configparser.ConfigParser()

config.read('conf_file', encoding='utf-8')

ret = config.sections()  # 获取所有节点 返回一个列表

ret1 = config.items('section1')  # 读取节点下的键值对

ret2 = config.options('section1')  # 读取某个节点下的键

  

print(ret)

print(ret1)

print(ret2)

  

# 2、读取节点键值

v = config.get('section1', 'k1')  # 获取指定key下的值 默认 str 类型

# v = config.getint('section1', 'k1')

# v = config.getfloat('section1', 'k1')

# v = config.getboolean('section1', 'k1')

print(v, type(v))

  

# 3、检查 添加 删除节点

has_sec = config.has_section('section1')

print(has_sec)

  

# config.add_section('section5')

# config.write(open('conf_file', 'w'))

  

# config.remove_section('section3')

# config.write(open('conf_file', 'w'))

  

# 4、检查 删除 设置 指定组内的键值对

has_opt = config.has_option('section1', 'k1')

print(has_opt)

  

# config.remove_option('section2', 'k3')

# config.write(open('conf_file', 'w'))

  

config.set('section5', 'k1', '123')

config.write(open('conf_file', 'w'))




re 模块

.  匹配除换行符以外的任意字符

\w 匹配字母或数字或下划线或汉字

\s 匹配任意的空白符 

\d 匹配数字

\b 匹配单词的开始或结束 

^  匹配字符串的开始

$  匹配字符串的结束 

*  重复零次或更多次

+  重复一次或更多次 

?  重复零次或一次

{n}   重复n次 

{n,}  重复n次或更多次

{n,m} 重复n到m次 



# import re

   

# match

# print(re.match('com', 'comwww.runcombb').group())  # match 匹配起始位置

# print(re.search('com', 'www.runcombb').group())  # search 匹配第一次位置

  

# sub subn 匹配 替换

# print(re.sub("g.t", "have", 'I get A, get B', 1))  # 1表示只替换1次

# print(re.subn("g.t", "have", 'I get A, get B'))  # 提示替换了几次

  

# split

# print(re.split('\d+', 'one1two2three3four4'))  # 有空格

# 输出

# ['one', 'two', 'three', 'four', '']

  

# compile 封装一个固定匹配规则供多次调用

# s = "JGood is a boy,so cool..."

# r = re.compile(r'\w*oo\w*')   # 查找所有包含oo的单词

# print(r.findall(s))

# 输出:

# ['JGood', 'cool']

  

# 反斜杠

# 在Python中 要进行两次转义才能匹配一个带反斜杠的字符 所以需要4个 \\\\

# print(re.search("\\\\com", "\comcn").group())

  

# 单词

# print(re.findall(r'I\b', 'I&am Ikobe')) # 有很多字符可以用来分隔单词 这里使用&

  

# 分组

# 去已经匹配到的数据中再提取数据

# origin = 'has sdfsdfsdfwer432'

# r = re.match("h\w+", origin)  # 输出:has () {}

# r = re.match("h(\w+)", origin)  # 输出:has ('as',) {}

# r = re.match("h(?P<name>\w+)", origin)  # 输出:has ('as',) {'name': 'as'}

# print(r.group())

# print(r.groups())

# print(r.groupdict())

  

# findall 分组

# origin = "hasaabc halaaabc"

# r = re.findall("h(\w+)a(ab)c", origin)  # 首先整体匹配 再将分组放入结果

# print(r)

# 输出:

# [('as', 'ab'), ('ala', 'ab')]

  

# spilt 分组

# origin = "hello alex abc alex age"

# r = re.split("a(le)x", origin, 1)  # 忽略了alex 直接匹配le

# print(r)

# 输出:

# ['hello ', 'le', ' abc alex age']

常用正则表达式


# IP:

# ^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$

  

# 手机号:

# ^1[3|4|5|8][0-9]\d{8}$

  

# 邮箱:

# [a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+



hashlib


# import hashlib

  

# obj = hashlib.md5(bytes('sdfsdfsadf', encoding='utf-8'))  # 加bytes任意字符防止被撞库破译

# obj.update(bytes('123', encoding='utf-8'))

# r = obj.hexdigest()

# print(r)

  

# python内置还有一个 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密

# import hmac 

  

# h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))

# h.update(bytes('admin',encoding="utf-8"))

# print(h.hexdigest())


os 系统级别的操作

os.getcwd()                 获取当前工作目录,即当前python脚本工作的目录路径

os.chdir("dirname")         改变当前脚本工作目录;相当于shell下cd

os.curdir                   返回当前目录: ('.')

os.pardir                   获取当前目录的父目录字符串名:('..')

os.makedirs('dir1/dir2')    可生成多层递归目录

os.removedirs('dirname1')   若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推

os.mkdir('dirname')         生成单级目录;相当于shell中mkdir dirname

os.rmdir('dirname')         删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname

os.listdir('dirname')       列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印

os.remove()                 删除一个文件

os.rename("oldname","new")  重命名文件/目录

os.stat('path/filename')    获取文件/目录信息

os.sep                      操作系统特定的路径分隔符,win下为"\\",Linux下为"/"

os.linesep                  当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"

os.pathsep                  用于分割文件路径的字符串

os.name                     字符串指示当前使用平台。win->'nt'; Linux->'posix'

os.system("bash command")   运行shell命令,直接显示

os.environ                  获取系统环境变量

os.path.abspath(path)       返回path规范化的绝对路径

os.path.split(path)         将path分割成目录和文件名二元组返回

os.path.dirname(path)       返回path的目录。其实就是os.path.split(path)的第一个元素

os.path.basename(path)      返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素

os.path.exists(path)        如果path存在,返回True;如果path不存在,返回False

os.path.isabs(path)         如果path是绝对路径,返回True

os.path.isfile(path)        如果path是一个存在的文件,返回True。否则返回False

os.path.isdir(path)         如果path是一个存在的目录,则返回True。否则返回False

os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略

os.path.getatime(path)      返回path所指向的文件或者目录的最后存取时间

os.path.getmtime(path)      返回path所指向的文件或者目录的最后修改时间



sys

用于对Python解释器相关操作:

sys.argv           命令行参数List,第一个元素是程序本身路径

sys.exit(n)        退出程序,正常退出时exit(0)

sys.version        获取Python解释程序的版本信息

sys.maxint         最大的Int值

sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值

sys.platform       返回操作系统平台名称

sys.stdin          输入相关

sys.stdout         输出相关

sys.stderror       错误相关

关于sys运用:进度条


def view_bar(num, total):

    rate = num / total

    rate_num = int(rate * 100)

    r1 = '\r%s>%d%%' % ("="*num, rate_num,)  # 加 r 的话让每次输出回到初始最前面位置

    sys.stdout.write(r1)  # 和print的区别就是不加换行符

    sys.stdout.flush()  # 清空屏幕输出

  

for i in range(0, 101):

    time.sleep(0.1)

    view_bar(i, 100) 


python的内置模块中对于命令行的解析模块共两个getopt 和 optparse 。不过getopt过于简单,往往不能满足需求。此时可以使用optparse模块。这个模块相对于getopt更新,功能更强大。

那么如何使用optparse模块呢? optparse 模块的官方文档给出了很详细的说明。


第一步、导入模块,并在主函数中创建实例


from optparse import OptionParser

[...]

parser = OptionParser()


第二步、使用add_option定义需要的option


parser.add_option(opt_str, ...,

                  attr=value, ...)


add_option 方法中有很多可选的参数,及一些影响optparse函数行为的属性。这些东西都很值得去细细推敲。


最后一步、当定义完所有option 后,通过parse_args 去解析所有的option。并返回解析结果


(options, args) = parser.parse_args()


parse_args 默认解析的是sys.argv[1:] 的所有参数。不过若你喜欢,也可以自己传递参数到parse_args。例如如下的形式:

args = ["-f", "foo.txt"]

(options, args) = parser.parse_args(args)


parse_args 有两个返回值,options 和 args 。其中options是一个对象,通过这个对象可以获取到所有定义的option相应信息。而args是一个list,里面存储了所有没有被定义的参数信息。


以上三个步骤,就是使用optparse模块的完整体现。不过在第二步中add_option中存在很多影响pars_args行为的属性,将在下面逐步记录解释。



action 属性:

它将告诉optparse 遇到相应的命令行时应该怎么去做。默认若不指定action属性,它将被赋予默认值store。那么store是什么意义呢?以官方的实例说明

当添加如下的option:


parser.add_option("-f", "--file",

                  action="store", type="string", dest="filename")

且传递如下的参数:


args = ["-f", "foo.txt"]

(options, args) = parser.parse_args(args)

当optparse 发现参数-f 时,它会将-f后面的一个参数也消费掉(将-f 和 foo.txt绑定到一起了)。并将foo.txt存储到options.filename中。当经过parse_args解析后,调用options.filename时将得到foo.txt这个值。


以上是action的默认值store。另外还有布尔类型的action。这样类型的东西主要是在命令行参数不需要值的时候使用。例如 -v 查看版本号, -v 后面就需要再写参数了。

Example:


parser.add_option("-v", action="store_true", dest="verbose")

parser.add_option("-q", action="store_false", dest="quit")

以上两个例子,当经过parse_args后调用options.verbose将为true。而调用options.quit将为false


当然,action还有其他一些值。如:store_const、append、count 和 callback 。研究后再呈上文章吧。

default属性:

给相应的参数设置默认值,也是一个很有必要知道的属性

Example:


parser.add_option("-v", action="store_true", dest="verbose", default=False)

parser.add_option("-q", action="store_false", dest="verbose", default=True)

另外一种比较清晰的方法设置默认值:


parser.set_defaults(verbose=True)

parser.add_option(...)

(options, args) = parser.parse_args()




optparse,是一个能够让程式设计人员轻松设计出简单明了、易于使用、符合标准的Unix命令列程式的Python模块。生成使用和帮助信息。

使用此模块前,首先需要导入模块中的类OptionParser,然后创建它的一个实例(对象):

 

 

from optparse import OptionParser

 

parser = OptionParser()  #这里也可以定义类的参数

例子1:


from optparse import OptionParser

  

def opt():

    parser=OptionParser("Usage: %prog -a command")

    parser.add_option('-a',

                      dest='addr',

                      action='store',

                      help='ip or iprange EX: 192.168.1,192.168.1.3 or192.168.1.1-192.168.1.100')

    options,args=parser.parse_args()

    return options, args

options,args=parser.parse_args()是一个方法返回的是一个元组里面包括选项和参数及options和args

例子2


#!/usr/bin/python

from optparse import OptionParser 

import sys

import os

parser = OptionParser() 

parser.add_option("-c","--char",   

                  dest="chars",

                  action="store_true",

                  default=False,

  

                  help="only count chars")

  

parser.add_option("-w", "--word",

                  dest="words",

                  action="store_true",

                  default=False,

                  help="only count words")

  

parser.add_option("-l", "--line",

                  dest="lines",

                  action="store_true",

                  default=False,

                  help="only count lines")

  

options, args=parser.parse_args()

print options,args


执行这个脚本 python  aa.py

{'chars': False, 'lines': False, 'words': False} []


[root@133 day1]# python hu.py -w hu.py

{'chars': False, 'lines': False, 'words': True}['hu.py']


这个hu.py就代表args  参数。大括号里面的代表options选项

注意:不要用模块的名字做脚本的名字,否则运行时会报错



#!/usr/bin/env python

#coding:utf-8

#对标准输入进行统计

import sys, os

from optparse import OptionParser

 

def opt():

    usage = "usage: %prog [options] arg1 arg2"

    parser = OptionParser()

    parser.add_option("-c", "--char",

                    dest="chars",

                    action="store_true",

                    default=False,

                    help="only count chars")

    parser.add_option("-w", "--word",

                    dest="words",

                    action="store_true",

                    default=False,

                    help="only count words")

    parser.add_option("-l", "--line",

                    dest="lines",

                    action="store_true",

                    default=False,

                    help="only count lines")

    parser.add_option("-n", "--nototal",

                    dest="nototal",

                    action="store_true",

                    default=False,

                    help="nototal")

    options, args = parser.parse_args()

 

    return options, args     

 

opt()

print sys.argv[:]    //打印出脚本运行时的参数,注意和parse_args返回的参数做对比

options, args = opt()

print options, args    //打印出parse_args返回的args的值。





#!/usr/bin/env python

# coding=utf-8

 

import sys, os

from optparse import OptionParser

 

def opt():

    parser = OptionParser()

    parser.add_option("-c", "--char",

                      dest="chars",

                      action="store_true",

                      default=False,

                      help="only count chars")

    parser.add_option("-w", "--ward",

                      dest="words",

                      action="store_true",

                      default=False,

                      help="only count words")

    parser.add_option("-l", "--line",

                      dest="lines",

                      action="store_true",

                      default=False,

                      help="only count lines")

    parser.add_option("-n", "--no-total",

                      dest="nototal",

                      action="store_true",

                      default=False,

                      help="show total or not")

    options, args = parser.parse_args()

    return options, args

 

def get_count(data):

    chars =  len(data)

    words = len(data.split())

    lines = data.count('\n')

    return lines, words, chars

 

def print_wc(options, lines, words, chars, fn):

    if options.lines:

        print lines,

    if options.words:

        print words,

    if options.chars:

        print chars,

    print(fn)

 

def main():

    options, args = opt()

    if not (options.words or options.chars or options.lines):

        options.words, options.chars, options.lines = True, True, True

 

    if args:

        total_lines, total_words, total_chars = 0, 0, 0

        for fn in args:

            if os.path.isfile(fn):

                with open(fn) as fd:

                    data = fd.read()

                lines, words, chars = get_count(data)

                print_wc(options, lines, words, chars, fn)

                total_lines += lines

                total_words += words

                total_chars += chars

            elif os.path.isdir(fn):

                print("%s: is a directory" % fn, file=sys.stderr)

            else:

                sys.stderr.write("%s: No such file or direcotry\n" % fn)

        if len(args) > 1 and not options.nototal:

            print_wc(options, total_lines, total_words, total_chars, 'total')

    else:

        data = sys.stdin.read()

        fn = ''

        lines, words, chars = get_count(data)

        print_wc(options, lines, words, chars, fn)

 

if __name__ == '__main__':

    main()



#!/usr/bin/env python

import sys, os

from optparse import OptionParser

 

parser = OptionParser()

parser.add_option("-c", "--char",

                  dest="chars",

                  action="store_true",

                  default=False,

                  help="only count chars")

 

parser.add_option("-w", "--word",

                  dest="words",

                  action="store_true",

                  default=False,

                  help="only count words")

 

parser.add_option("-l", "--line",

                  dest="lines",

                  action="store_true",

                  default=False,

                  help="only count lines")

 

 

options, args = parser.parse_args()

print options, args

 

data = sys.stdin.read()

chars = len(data)

words = len(data.split())

lines = data.count('\n')

 

if options.chars:

    print chars,

if options.words:

    print words,

if options.lines:

    print lines



parse_args()这个方法返回两个值,options和args,分别是对象和列表,options里包括所有使用parser.add_option()这个方法定义的选项,比如‘-w'。

options.words就是存储'-w'这个选项的,它的值是True或者False,比如脚本后面带-w选项时,那么options.words的值就是True。

下面这个在ipython下的输出,由于没有使用add_option()定义任何选项,所以options的输出里没有选项的值。

这个是python自带的模块,想具体了解它的内部是如何实现的,源码文件在这个位置,/usr/lib64/python2.6/optparse.py。


脚本中这样定义的:dest = "characters",

后面应该这样判断:if options.characters,而不是if options.chars



dest和action有什么用?看Help似乎没怎么提到?

在代码里引用选项时需要dest后面定义的那个名字,比如引用-c选项,就使用options.characters,每个选项都需要dest去定义一个名字,这个值就是选项的名字,目的就是在程序中去引用这个选项,比如:if not (options.characters or options.words or options.lines):括号里就是在引用这些选项。

有的命令后面的选项就是一个字母,有的不仅有字母,而且后面还有值,比较一下下面这两个命令:

wc -l /etc/passwd

tail -n 20 /etc/passwd

-l与-n都是选项,但是行为不一样,-l后面没有值,-n后面有值,那么选项后面带不带值是action决定的,如果action="store_true",那么说明选项后面没有值,如果action='store',说明选项后面需要带值。

脚本后面跟-h时,可以看到help定义的内容。



那default又是有什么作用,为False和True分别表示什么?

拿-c选项举例子,

default如果为True,表示脚本后面如果不加-c选项,默认也是有-c的行为的。

default为False时,表示脚本后面不加-c选项,就没有-c的行为,比如wc -l /etc/hosts,没有-c选项,就说明不对字符统计,只对行数统计




sftp是安全文件传输协议,提供一种安全的加密方法,sftp是SSH的一部分,SFTPClient类实现了sftp客户端,通过已建立的SSH通道传输文件,与其他的操作,如下:



sftp.getcwd()返回当前工作目录

sftp.chdir(path)改变工作目录

sftp.chmod(path, mode)修改权限

sftp.chown(path, uid, gid)设置属主属组

sftp.close()关闭sftp

sftp.file(filename, mode='r', bufsize=-1)读取文件

sftp.from_transport(s)创建SFTP客户端通道

sftp.listdir(path='.')列出目录,返回一个列表

sftp.listdir_attr(path='.')列出目录,返回一个SFTPAttributes列表

sftp.mkdir(path, mode=511)创建目录

sftp.normalize(path)返回规范化path

sftp.open(filename, mode='r', bufsize=-1)在远程服务器打开文件

sftp.put(localpath, remotepath, callback=None)localpath文件上传到远程服务器remotepath

sftp.get(remotepath, localpath, callback=None)从远程服务器remotepath拉文件到本地localpath

sftp.readlink(path)返回一个符号链接目标

sftp.remove(path)删除文件

sftp.rename(oldpath, newpath)重命名文件或目录

sftp.rmdir(path)删除目录

sftp.stat(path)返回远程服务器文件信息(返回一个对象的属性)

sftp.truncate(path, size)截取文件大小

sftp.symlink(source, dest)创建一个软链接(快捷方式)

sftp.unlink(path)删除软链接




fabric

fabric模块是在paramiko基础上又做了一层封装,操作起来更方便。主要用于多台主机批量执行任务。

默认Python没有,需要手动安装:pip install fabric

如安装失败,可以尝试yum安装:yum install fabric



Fabric常用API:

API类

描述

示例

local执行本地命令local('uname -s')

lcd切换本地目录lcd('/opt')

run执行远程命令run('uname -s')

cd切换远程目录cd('/opt')

sudosudo方式执行远程命令sudo('/etc/init.d/httpd start')

put上传本地文件或目录到远程主机put(remote_path, local_path)

get从远程主机下载文件或目录到本地put(local_path, remote_path)

open_shell打开一个shell,类似于SSH连接到了远程主机open_shell("ifconfig eth0")

prompt获得用户输入信息prompt('Please input user password: ')

confirm获得提示信息确认confirm('Continue[Y/N]?')

reboot重启远程主机reboot()

@task函数装饰器,引用说明函数可调用,否则不可见

@runs_once函数装饰器,函数只会执行一次

当我们写好fabric脚本后,需要用fab命令调用执行任务。

命令格式:fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...

fab命令有以下常用选项:

选项

描述

-l打印可用的命令(函数)

--set=KEY=VALUE,...逗号分隔,设置环境变量

--shortlist简短打印可用命令

-c PATH指定本地配置文件

-D不加载用户known_hosts文件

-f PATH指定fabfile文件

-g HOST逗号分隔要操作的主机

-i PATH指定私钥文件

-k不加载来自~/.ssh下的私钥文件

-p PASSWORD使用密码认证and/or sudo

-P默认为并行执行方法

--port=PORT指定SSH连接端口

-R ROLES根据角色操作,逗号分隔

-s SHELL指定新shell,默认是'/bin/bash -l -c'

--show=LEVELS以逗号分隔的输出

--ssh-config-path=PATHSSH配置文件路径

-t N设置连接超时时间,单位秒

-T N设置远程命令超时时间,单位秒

-u USER连接远程主机用户名

-x HOSTS以逗号分隔排除主机

-z INT并发进程数 




本地执行命令


from fabric.api import local

def command():

    local('ls')

# fab command

[localhost] local: ls

fabfile.py  fabfile.pyc  tab.py  tab.pyc

Done.



使用fab命令调用,默认寻找当前目录的fabfile.py文件。

from fabric.api import run

def command():

    run('ls')

# fab -H 192.168.1.120 -u user command

[192.168.1.120] Executing task 'command'

[192.168.1.120] run: ls

[192.168.1.120] Login password for 'user':

[192.168.1.120] out: access.log  a.py

[192.168.1.120] out:

Done.

 

Disconnecting from 192.168.1.120... done.



如果在多台主机执行,只需要-H后面的IP以逗号分隔即可



给脚本函数传入位置参数


from fabric.api import run


def hello(name="world"):

    print("Hello %s!" % name)

# fab -H localhost hello

[localhost] Executing task 'hello'

Hello world!

Done.

# fab -H localhost hello:name=Python

[localhost] Executing task 'hello'

Hello Python!

Done.


主机列表组


from fabric.api import run, env


env.hosts = ['root@192.168.1.120:22', 'root@192.168.1.130:22']

env.password = '123.com'

env.exclude_hosts = ['root@192.168.1.120:22']   # 排除主机

def command():

   run('ls')



env作用是定义fabfile全局设定,类似于变量。还有一些常用的属性:

env属性

描述

示例

env.hosts定义目标主机env.hosts = ['192.168.1.120:22']

env.exclude_hosts排除指定主机env.exclude_hosts = '[192.168.1.1]'

env.user定义用户名env.user='root'

env.port定义端口env.port='22'

env.password定义密码env.password='123'

env.passwords定义多个密码,不同主机对应不同密码env.passwords = {'root@192.168.1.120:22': '123'}

env.gateway定义网关env.gateway='192.168.1.2'

env.roledefs定义角色分组env.roledef = {'web':['192.168.1.11'], 'db':['192.168.1.12']}

env.deploy_release_dir自定义全局变量,格式:env.+ '变量名'env.var




# vi install.py


from fabric.api import run, env


env.roledefs = {

    'web': ['192.168.1.10', '192.168.1.20'],

    'db': ['192.168.1.30', '192.168.1.40']

}

env.password = '123'

@roles('web')

def task1():

   run('yum install httpd -y')

@roles('db')

def task2():

   run('yum install mysql-server -y')

def deploy():

   execute(task1)

   execute(task2)

# fab -f install.py deploy



上传目录到远程主机



from fabric.api import *

env.hosts = ['192.168.1.120']

env.user = 'user'

env.password = '123.com'

def task():

   put('/root/abc', '/home/user')

   run('ls -l /home/user')

# fab task



 从远程主机下载目录


from fabric.api import *


env.hosts = ['192.168.1.120']

env.user = 'user'

env.password = '123.com'

def task():

   get('/home/user/b', '/opt')

   local('ls -l /opt')

# fab task


打印颜色,有助于关键地方醒目


from fabric.colors import *


def show():

   print green('Successful.')

   print red('Failure!')

   print yellow('Warning.')

# fab show



pexpect

pexpect是一个用来启动子程序,并使用正则表达式对程序输出做出特定响应,以此实现与其自动交互的Python模块。暂不支持Windows下的Python环境执行。

这里主要讲解run()函数和spawn()类,能完成自动交互,下面简单了解下它们使用。


安装Python


download pexpect-2.3.tar.gz

tar zxvf pexpect-2.3.tar.g

cd pexpect-2.3

python setup.py install  (do this as root)


下载pexpect模块:https://pypi.python.org/pypi/pexpect/#downloads

解压后在目录下运行:python ./setup.py install (必须是root权限)

如果没有使用root权限,你只需要把lib的路径放入sys.path,这样便可以使用pexpect


import sys

sys.path.append('pexpect-4.2.1/build/lib')


确认是否安装成功

import pexpect

dir(pexpect)


['EOF', 'ExceptionPexpect', 'Expecter', 'PY3', 'TIMEOUT', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__revision__', '__version__', 'exceptions', 'expect', 'is_executable_file', 'searcher_re', 'searcher_string', 'split_command_line', 'sys', 'utils', 'which']




18.3.1 run()

run()函数用来运行bash命令,类似于os模块中的system()函数。


参数:run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)


run(command,timeout=-1,withexitstatus=False,events=None,extra_args=None,logfile=None, cwd=None, env=None)


函数 run 可以用来运行命令,其作用与 Python os 模块中 system() 函数相似。

run() 是通过 Pexpect 类实现的。


如果命令的路径没有完全给出,则 run 会使用 which 命令尝试搜索命令的路径 。



例1:执行ls命令

>>> import pexpect

>>> pexpect.run("ls") 


例2:获得命令状态返回值

>>> command_output, exitstatus = pexpect.run("ls", withexitstatus=1)


command_outout是执行结果,exitstatus是退出状态值。


使用 run()执行 svn 命令


from pexpect import *


run ("svn ci -m 'automatic commit' my_file.py")


与 os.system() 不同的是,使用 run() 可以方便地同时获得命令的输出结果与命令的退出状态 。


run() 的返回值


from pexpect import *


(command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1)


command_out 中保存的就是 /bin 目录下的内容



spawn()是pexpect模块主要的类,实现启动子程序,使用pty.fork()生成子进程,并调用exec()系列函数执行命令。


参数:spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)


spawn()类几个常用函数:


expect(pattern, timeout=-1, searchwindowsize=None) #匹配正则表达式,pattern可以是正则表达式。


send(s)给子进程发送一个字符串


sendline(s='')就像send(),但添加了一个换行符(os.lineseq)


sendcontrol(char)发送一个控制符,比如ctrl-c、ctrl-d



spawn() 使用示例


child = pexpect.spawn ('/usr/bin/ftp') #执行ftp客户端命令


child = pexpect.spawn ('/usr/bin/ssh user@example.com') #使用ssh登录目标机器


child = pexpect.spawn ('ls -latr /tmp') #显示 /tmp目录内容


当子程序需要参数时,还可以使用一个参数的列表:


child = pexpect.spawn ('/usr/bin/ftp', [])

child = pexpect.spawn ('/usr/bin/ssh', ['user@example.com'])

child = pexpect.spawn ('ls', ['-latr', '/tmp'])


在构造函数中,maxread 属性指定了 Pexpect 对象试图从 tty 一次读取的最大字节数,它的默认值是2000字节 。

由于需要实现不断匹配子程序输出, searchwindowsize 指定了从输入缓冲区中进行模式匹配的位置,默认从开始匹配。

logfile 参数指定了 Pexpect 产生的日志的记录位置。

记录日志

child = pexpect.spawn('some_command')

fout = file('mylog.txt','w')

child.logfile = fout


还可以将日志指向标准输出:

child = pexpect.spawn('some_command')

child.logfile = sys.stdout

如果不需要记录向子程序输入的日志,只记录子程序的输出,可以使用:

child = pexpect.spawn('some_command')


child.logfile_send = sys.stdout



ftp交互

用ftp命令登录是这样的,需要手动输入用户名和密码,才能登录进去。


# ftp 192.168.1.10

Connected to 192.168.1.10 (192.168.1.10).

220-FileZilla Server version 0.9.46 beta

220-written by Tim Kosse (tim.kosse@filezilla-project.org)

220 Please visit http://sourceforge.net/projects/filezilla/

Name (192.168.1.10:root): yunwei

331 Password required for yunwei

Password:

230 Logged on

Remote system type is UNIX.

ftp>


下面我们用pexpect帮我们完成输入用户名和密码:


import pexpect


child = pexpect.spawn('ftp 192.168.1.10')

child.expect('Name .*: ')

child.sendline('yunwei')

child.expect('Password:')

child.sendline('yunweipass')

child.expect('ftp> ')

child.sendline('ls')

child.sendline('bye')

child.expect(pexpect.EOF)   # pexpect.EOF程序打印提示信息

print child.before   # 保存命令执行结果




Python 远程批量修改密码脚本


#tar -zxvf pexpect-3.0.tar.gz

#cd pexpect-3.0

#python setup.py install

#!/usr/bin/env python

#coding:utf8

import pexpect                            

import sys 

iplist = ['192.168.140.142','192.168.140.145'] ##定义主机列表

oldpasswd = '234567' ##旧密码

newpasswd = '1234567' ##新密码

while iplist:

    ip = iplist[-1] ##获取一个IP

    iplist.pop() ##列表去掉一个值

    child = pexpect.spawn('ssh root@'+ip) ##定义触发

    fout = file('passlog.txt','a') ##定义日志文件,

    child.logfile = fout

    try:

        while True:

            index = child.expect(['(yes/no)','(?i)password:'])

            if index == 0:

                child.sendline('yes')

            elif index == 1:

                child.sendline(oldpasswd)

                child.expect('#')

                child.sendline('echo  '+newpasswd+' | passwd --stdin root')

                child.expect('#')

                child.sendline('exit')

    except pexpect.TIMEOUT:

        print >>sys.stderr, ip+' timeout'

    except pexpect.EOF:

        print >>sys.stderr, ip+' <the end>'

(1)spawn类

 class pexpect.spawn(command,args=[],timeout=30,maxread=2000,searchwidowsize=None

,logfile=None,cwd=None,env=None,ignore_sighup=True)

(2)run函数

pexpect.run(command,timeout=-1,withexitstatus=False,events=None,extra_args=None,

logfile=None,cwd=None,env=None).

(3)pxssh类

class pexpect.pxssh.pxssh(timeout=30,maxread=2000,searchwidowsize=None,logfile=None,

cwd=None,env=None)

pxssh常用的三个方法:

    login()建立连接;

    logout()断开连接;

    prompt()等待系统提示符,用于等待命令执行结束



python之pexpect用法及scp新用途


import pexpect

def scp_cmd():

    passwd='*******'

    passwd1='*******'

    ssh = pexpect.spawn('scp -rp root@192.168.1.107:/backup root@192.168.1.102:/data')

    r = ''

    try:

        i = ssh.expect(['password: ', 'continue connecting (yes/no)?'])

        if i == 0 :

            ssh.sendline(passwd)

        elif i == 1:

            ssh.sendline('yes')

            ssh.expect('password:')

            ssh.sendline(passwd)

        b=ssh.expect(['password: ','continue connecting (yes/no)?'])

        if b==0:

            ssh.sendline(passwd1)

        elif b==1:

            ssh.sendline('yes')

            ssh.expect('password:')

            ssh.sendline(passwd1)

    except pexpect.EOF:

        ssh.close()

    else:

        r = ssh.read()

        ssh.expect(pexpect.EOF)

        ssh.close()

    return r

scp_cmd()




python安装setuptools模块之后,便可使用easy_install来安装python的第三方扩展模块,默认安装路径是:

/usr/lib/python2.6/site-packages/


easy_install 模块名 #可以直接安装

easy_install  pexpect

[root@zhu ~]# easy_install pexpect

Searching for pexpect

Reading http://pypi.python.org/simple/pexpect/

Reading http://pexpect.readthedocs.org/

Reading http://pexpect.sourceforge.net/

Reading http://sourceforge.net/project/showfiles.php?group_id=59762

Best match: pexpect 3.1

Downloading https://pypi.python.org/packages/source/p/pexpect/pexpect-3.1.tar.gz#md5=5a8e1573062e2e2c203c9a6d213b16e7

Processing pexpect-3.1.tar.gz

Running pexpect-3.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-KOPmVQ/pexpect-3.1/egg-dist-tmp-GnQBTg

zip_safe flag not set; analyzing archive contents...

Adding pexpect 3.1 to easy-install.pth file

Installed /usr/lib/python2.6/site-packages/pexpect-3.1-py2.6.egg

Processing dependencies for pexpect

Finished processing dependencies for pexpect


#由于pexpect-3.1-py2.6.egg此时仍是一个压缩文件,所以需要进行解压。

cd /usr/lib/python2.6/site-packages/

unzip pexpect-3.1-py2.6.egg



pexpect是一个用来启动子程序并对其进行自动控制的python模块,可以用来和ssh,ftp,telnet等需要输入密码的命令行程序进行自动交互。

安装过程如上。


另一种安装方法如下:



wget http://pexpect.sourceforge.net/pexpect-2.3.tar.gz

 tar xzf pexpect-2.3.tar.gz

 cd pexpect-2.3

 python ./setup.py install



pexpect 模块的使用如下:



>>> pexpect.

pexpect.EOF(                 pexpect.__path__             pexpect.run(

pexpect.ExceptionPexpect(    pexpect.__reduce__(          pexpect.runu(

pexpect.PY3                  pexpect.__reduce_ex__(       pexpect.searcher_re(

pexpect.TIMEOUT(             pexpect.__repr__(            pexpect.searcher_string(

pexpect.__all__              pexpect.__revision__         pexpect.select

pexpect.__class__(           pexpect.__setattr__(         pexpect.signal

pexpect.__delattr__(         pexpect.__sizeof__(          pexpect.spawn(

pexpect.__dict__             pexpect.__str__(             pexpect.spawnu(

pexpect.__doc__              pexpect.__subclasshook__(    pexpect.split_command_line(

pexpect.__file__             pexpect.__version__          pexpect.struct

pexpect.__format__(          pexpect._run(                pexpect.sys

pexpect.__getattribute__(    pexpect.codecs               pexpect.termios

pexpect.__hash__(            pexpect.errno                pexpect.time

pexpect.__init__(            pexpect.fcntl                pexpect.traceback

pexpect.__loader__           pexpect.os                   pexpect.tty

pexpect.__name__             pexpect.pty                  pexpect.types

pexpect.__new__(             pexpect.re                   pexpect.which(

pexpect.__package__          pexpect.resource


1.pexpect.run()函数的使用



run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)


#默认情况下该指令:

#1.运行给出的指令command,如果指令不是以绝对路径给出,会自动在PATH中寻找。结果输出作为字符串返回,行与行之间以\r\n分割。

#2.withexitstatus设置为True时,结果返回一个元组,(command_output,

    exitstatus)


#events是一个字典,有模式和应答组成,可以自动输入内容,如果应答需要输入enter键时,此时需要为一个新行,即添加\n.

>>> pexpect.run('cat /root/a.txt')

'qian shan\r\nniao fei jue\r\ndu diao han jiang xue\r\n'

>>> pexpect.run('cat /root/a.txt',withexitstatus=1)

('qian shan\r\nniao fei jue\r\ndu diao han jiang xue\r\n', 0)

>>> pexpect.run('scp /root/a.txt 192.168.56.102:/root',withexitstatus=1,events={'password': '123456\n'})

("root@192.168.56.102's password: \r\n\ra.txt                                           0%    0     0.0KB/s   --:-- ETA\ra.txt                                         100%   45     0.0KB/s   00:00    \r\n", 0)





2.pexpect.spawn()类的使用

spawn是Pexpect模块主要的类,用以实现启动子程序,它有丰富的方法与子程序交互从而实现用户对子程序的控制。它主要使用 pty.fork() 生成子进程,并调用 exec() 系列函数执行 command 参数的内容。



child = pexpect.spawn ('/usr/bin/ftp') #执行ftp客户端命令

child = pexpect.spawn ('/usr/bin/ssh user@example.com') #使用ssh登录目标机器

child = pexpect.spawn ('ls -latr /tmp') #显示 /tmp目录内容

child = pexpect.spawn ('/usr/bin/ftp', [])

child = pexpect.spawn ('/usr/bin/ssh', ['user@example.com'])

child = pexpect.spawn ('ls', ['-latr', '/tmp'])

child = pexpect.spawn('some_command')

fout = file('mylog.txt','w')


child.logfile = fout



child = pexpect.spawn('ssh root@192.168.56.102')


child.expect(self, pattern, timeout=-1, searchwindowsize=-1)


为了控制子程序,等待子程序产生特定输出,做出特定的响应,可以使用 expect 方法

在参数中: pattern 可以是正则表达式, pexpect.EOF , pexpect.TIMEOUT ,或者由这些元素组成的列表。需要注意的是,当 pattern 的类型是一个列表时,且子程序输出结果中不止一个被匹配成功,则匹配返回的结果是缓冲区中最先出现的那个元素,或者是列表中最左边的元素。使用 timeout 可以指定等待结果的超时时间 ,该时间以秒为单位。当超过预订时间时, expect 匹配到pexpect.TIMEOUT。



3.打印before的内容


>>> child = pexpect.spawn('ls -l')

>>> child.expect(pexpect.EOF)

0

>>> print child.before

总用量 64

drwxr-xr-x.  3 root root  4096 4月   2 10:09 aaa

-rw-r--r--.  1 root root    45 4月   3 15:01 a.txt

drwxr-xr-x. 16 root root  4096 3月   6 21:36 biaozhunku

drwxr-xr-x.  2 root root  4096 3月  27 17:03 mypython

drwxr-xr-x.  2 root root  4096 4月   3 13:21 mysource

drwxr-xr-x.  2 root root  4096 4月   3 13:20 mywork

drwxr-xr-x.  2 root root 36864 3月  19 11:09 pythoncook

-rw-r--r--.  1 root root   276 4月   3 14:26 zhu.py

#child.before 保存的就是在根目录下执行 ls 命令的结果


send(self, s)

sendline(self, s='')

sendcontrol(self, char)


这些方法用来向子程序发送命令,模拟输入命令的行为。与 send() 不同的是 sendline() 会额外输入一个回车符 ,更加适合用来模拟对子程序进行输入命令的操作。当需要模拟发送 “Ctrl+c” 的行为时,还可以使用 sendcontrol() 发送控制字符。



手动输入时,是来自键盘的标准输入,而pexpect是先匹配到关键字,再向子进程发送字符串。

pexpect.EOF打印提示信息,child.before保存的是命令执行结果。


通过上面的例子想必你已经知道pexpect主要功能了,在交互场景下很有用,这里就讲解这么多了,目的是给大家提供一个自动交互实现思路。

小结:

通过对Python下paramiko、fabric和pexpect模块使用,它们各有自己擅长的一面。

paramiko:方便嵌套系统平台中,擅长远程执行命令,文件传输。

fabric:方便与shell脚本结合,擅长批量部署,任务管理。

pexpect:擅长自动交互,比如ssh、ftp、telnet。










本文转自 chengxuyonghu 51CTO博客,原文链接:http://blog.51cto.com/6226001001/1902645,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
测试技术 网络安全 数据安全/隐私保护
Paramiko是一个用于处理SSHv2协议的Python库
Paramiko是一个用于处理SSHv2协议的Python库
37 3
|
3月前
|
Unix Linux 网络安全
python中连接linux好用的模块paramiko(附带案例)
该文章详细介绍了如何使用Python的Paramiko模块来连接Linux服务器,包括安装配置及通过密码或密钥进行身份验证的示例。
132 1
|
4月前
|
SQL 关系型数据库 MySQL
Python之MySQL操作及Paramiko模块操作
Python之MySQL操作及Paramiko模块操作
|
5月前
|
数据挖掘 Python
🚀告别繁琐!Python I/O管理实战,文件读写效率飙升的秘密
【7月更文挑战第29天】在 Python 编程中,高效的文件 I/O 对性能至关重要。
51 4
|
5月前
|
数据挖掘 数据处理 Python
🔍深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
【7月更文挑战第29天】深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
42 3
|
5月前
|
安全 数据安全/隐私保护 Python
|
5月前
|
JSON 监控 开发者
Python I/O管理新篇章:优化你的程序,让数据流动更顺畅
【7月更文挑战第30天】在数据驱动时代, Python I/O操作效率至关重要。理解I/O瓶颈,使用缓冲技术(如调整`open`的`buffering`参数),并发与异步I/O(借助`asyncio`),高效序列化(json, msgpack),及监控调试(cProfile)能显著提升性能。示例展示了缓冲读取和异步文件操作的最佳实践。不断学习可助开发者优化数据流。
72 2
|
5月前
|
API Python
Python高手修炼手册:精通文件系统操作,掌控I/O管理,提升编程效率
【7月更文挑战第30天】在 Python 编程中, 文件系统操作与 I/O 管理是连接程序与数据的关键。初学者常因路径错误和权限问题受挫, 而高手能自如管理文件。传统 `os` 和 `os.path` 模块易出错, `pathlib` 提供了更直观的对象导向 API。I/O 方面, 同步操作会阻塞程序, 异步 (如使用 `aiofiles`) 则能大幅提升并发能力。真正的高手不仅掌握 API, 更能预见性能瓶颈并优化代码, 实现高效与优雅。
45 1
|
5月前
|
监控 Python
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息