Python批量管理主机(paramik

发布时间:2019-08-27 07:59:30编辑:auto阅读(2264)

    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。


关键字