• Shutil模块介绍


    引言

    在看django和scrapy源码的时候,可以看见他会有一个模式,即先有一些模块文件,在他的源码中,当你使用他规定的命令的时候,就会复制这些文件,来生成你要的模板文件。这里就是用了python自带的shutil模块。

    回想

    曾经自己写过一个复制备份的模块,当时写的时候还觉得自己太NB了,还能兼顾linux,windows的兼容性,使用了windows的copy命令和linux的cp命令,用python来调用这些命令,现在想想挺搞笑,其实完全可以用shutil模块来,哎,没文化真可怕。不过开发的过程也是一个体会的过程,不是么,说不定shutil模块也是使用了这些命令呢, 当然,这是猜测,实际上不是。但是会有那么一点影子的。后面我会给出分析。

    基本功能介绍

    这一点想获得更多知识请参看api,这里我只说一些常用的。

    1.    基本复制方法

    采用给出2个文件对象的方式,在2个文件对象之间进行数据复制达到目的。

    copyfileobj源码:

    def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
    buf = fsrc.read(length)
    if not buf:
    break
    fdst.write(buf)

    分析:给出2个文件对象,通过读取原文件的内容,写入到新文件对象中,每次写入16KB。

    这个方法实际是不常用的,而是为了我们的常用方式做准备的。注意这个方法这里没有流文件对象并没有关闭,即这确实只是一个基础方法。

    Copyfile源码

    def copyfile(src, dst):
    """Copy data from src to dst"""
    if _samefile(src, dst):
    raise Error, "`%s` and `%s` are the same file" % (src, dst)

    fsrc = None
    fdst = None
    try:
    fsrc = open(src, 'rb')
    fdst = open(dst, 'wb')
    copyfileobj(fsrc, fdst)
    finally:
    if fdst:
    fdst.close()
    if fsrc:
    fsrc.close()

    这里代码没有任何难度,读取2个文件对象,调用刚才的copyfileobject对象。

    测试.

    条件:

    E:\test\a文件夹下有一个文件jquery.min.js

    E:\test\b下没有任何文件,但必须指定一个文件名

    #! -*- encoding:utf-8 -*-
    import shutil
    shutil.copyfile("E:\\test\\a\\jquery.min.js", "E:\\test\\b\\jquery.min.js")

    执行结果在E:\test\b目录下生成了一个名为jquery.min.js的文件。

    外部调用方法

    Copy源码

    def copy(src, dst):
    """Copy data and mode bits ("cp src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
    dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copymode(src, dst)

    可以看到这里有有趣的一行注释copy data and mode bits(“cp src dst”)复制文件内容的执行模式,完成的功能类似于cp src dst,在linux中不就是这个命令么,当然linux最终底层怎么实现的不得而知。 猜测也差不了多少。

    代码解释,这里有一个条件,即如果dst是文件夹,而不是文件对象,那么就使用原来文件的文件名。即这个copy方法可以不用管是否拷贝对象是否是一个完整路径,文件夹也行,只不过文件夹的话,就以原来的文件名为新文件的文件名了。

    测试,将刚才上个测试程序b文件夹中的文件清除,可以执行下面程序。会有新文件复制成功。

    #! -*- encoding:utf-8 -*-
    import shutil

    shutil.copy("E:\\test\\a\\jquery.min.js", "E:\\test\\b")

    Copy2源码

    def copy2(src, dst):
    """Copy data and all stat info ("cp -p src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
    dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copystat(src, dst)

    对比copy和copy2,发现只有copyfile下面的方法变了,而注释变成了cp –p src dst,熟悉linux的同学应该了解各种参数意义。

    这里将原有文件的所有属性状态都copy过去了

    Copytree源码

    def copytree(src, dst, symlinks=False, ignore=None):
        names = os.listdir(src)
        if ignore is not None:
            ignored_names = ignore(src, names)
        else:
            ignored_names = set()
    
        os.makedirs(dst)
        errors = []
        for name in names:
            if name in ignored_names:
                continue
            srcname = os.path.join(src, name)
            dstname = os.path.join(dst, name)
            try:
                if symlinks and os.path.islink(srcname):
                    linkto = os.readlink(srcname)
                    os.symlink(linkto, dstname)
                elif os.path.isdir(srcname):
                    copytree(srcname, dstname, symlinks, ignore)
                else:
                    copy2(srcname, dstname)
                # XXX What about devices, sockets etc.?
            except (IOError, os.error), why:
                errors.append((srcname, dstname, str(why)))
            # catch the Error from the recursive copytree so that we can
            # continue with other files
            except Error, err:
                errors.extend(err.args[0])
        try:
            copystat(src, dst)
        except OSError, why:
            if WindowsError is not None and isinstance(why, WindowsError):
                # Copying file access times may fail on Windows
                pass
            else:
                errors.extend((src, dst, str(why)))
        if errors:
            raise Error, errors
    

      

    该方法给出一个原始文件夹系统,下面可以有N个文件夹和文件,给出dst,即给出你想copy的路径的根路径,注意,这个根路径当前是必须不存在的,源码中标注红色部分,如果存在,会产生错误。这一点上,感觉该做一个条件判断的,可惜没做。当然不是大问题。有点吹毛求疵了。

    测试:

    E:\test\a 在a 目录下任意新建文件夹和文件,N多层次,test下也只有a这个文件夹。

    #! -*- encoding:utf-8 -*-
    import shutil

    shutil.copytree("E:\\test\\a", "E:\\test\\b")

    执行后会在test文件夹下多出一个b文件夹,并且b文件夹下有a文件夹下的所有内容.

    Rmtree源码

    def rmtree(path, ignore_errors=False, onerror=None):
    if ignore_errors:
    def onerror(*args):
    pass
    elif onerror is None:
    def onerror(*args):
    raise
    try:
    if os.path.islink(path):
    # symlinks to directories are forbidden, see bug #1669
    raise OSError("Cannot call rmtree on a symbolic link")
    except OSError:
    onerror(os.path.islink, path, sys.exc_info())
    # can't continue even if onerror hook returns
    return
    names = []
    try:
    names = os.listdir(path)
    except os.error, err:
    onerror(os.listdir, path, sys.exc_info())
    for name in names:
    fullname = os.path.join(path, name)
    try:
    mode = os.lstat(fullname).st_mode
    except os.error:
    mode = 0
    if stat.S_ISDIR(mode):
    rmtree(fullname, ignore_errors, onerror)
    else:
    try:
    os.remove(fullname)
    except os.error, err:
    onerror(os.remove, fullname, sys.exc_info())
    try:
    os.rmdir(path)
    except os.error:
    onerror(os.rmdir, path, sys.exc_info())

    我想看名字你就该知道这个方法是干嘛的了。

    刚才copytree执行成功后立即执行下面的代码:

    #! -*- encoding:utf-8 -*-
    import shutil

    shutil.rmtree("E:\\test\\b")

    可以发现b文件夹连同下面的文件都消失了。

    Move源码

    def move(src, dst):
    real_dst = dst
    if os.path.isdir(dst):
    real_dst = os.path.join(dst, _basename(src))
    if os.path.exists(real_dst):
    raise Error, "Destination path '%s' already exists" % real_dst
    try:
    os.rename(src, real_dst)
    except OSError:
    if os.path.isdir(src):
    if destinsrc(src, dst):
    raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
    copytree(src, real_dst, symlinks=True)
    rmtree(src)
    else:
    copy2(src, real_dst)
    os.unlink(src)

    同上,看名字就知道的功能,类似于windows的ctrl+x->ctrl+v操作。

    测试。

    执行完rmtree后,test目录只有一个a文件夹,执行下面程序,可以看到a文件夹没有了,取而代之的是b文件夹下有a文件夹的所有内容。有点想os.rename了,但是只是因为我将这2个测试文件都放在了一起而已,即他能比较笨的完成os.rename的功能,但os.rename不可能会做move的功能。

    #! -*- encoding:utf-8 -*-
    import shutil
    shutil.move("E:\\test\\a", "E:\\test\\b")

    最后,解释下这个模块的名字,shutil , shu+til?中国人相信第一次看见都那么分的, 从上面分析的功能看应该是sh+util,即完成shell的一些功能的工具集。




















  • 相关阅读:
    IOS基于 fmdb数据库 的简单操作应用
    App 上线被拒绝的原因有哪些?
    cocoaPods的安装以及使用
    IOS开发工程师的近期面试题
    UIButton基本介绍
    UIView 详解
    使用 Paros 抓接口
    如何在main方法中创建50000次对象
    如何将将String 转换 int
    2020年09月24号--测试登录账号15分钟有效时长
  • 原文地址:https://www.cnblogs.com/CLTANG/p/2249257.html
Copyright © 2020-2023  润新知