Python 覆盖以前提取的文件而不是创建新文件

Python 覆盖以前提取的文件而不是创建新文件,python,file,extract,overwrite,ziparchive,Python,File,Extract,Overwrite,Ziparchive,有几个库用于通过Python提取归档文件,例如gzip、zipfile库、rarfile、tarfile、patool等。我发现其中一个库(patool)特别有用,因为它具有跨格式特性,可以提取几乎任何类型的归档文件,包括最流行的ZIP、gzip、,焦油和稀薄油 要使用patool提取存档文件,非常简单: patoolib.extract_archive( "Archive.zip",outdir="Folder1") 其中,“Archive.zip”是存档文件的路径,“Folder1”是存储

有几个库用于通过Python提取归档文件,例如gzip、zipfile库、rarfile、tarfile、patool等。我发现其中一个库(patool)特别有用,因为它具有跨格式特性,可以提取几乎任何类型的归档文件,包括最流行的ZIP、gzip、,焦油和稀薄油

要使用patool提取存档文件,非常简单:

patoolib.extract_archive( "Archive.zip",outdir="Folder1")
其中,
“Archive.zip”
是存档文件的路径,
“Folder1”
是存储解压缩文件的目录的路径

提取效果很好。问题是,如果我为完全相同的归档文件再次运行相同的代码,则相同的提取文件将存储在同一文件夹中,但名称略有不同(第一次运行时为filename,第二次运行时为filename1,第三次运行时为filename11,依此类推)

如果目录中已经存在同名文件,则需要代码来覆盖提取的文件

这个
extract\u archive
函数看起来非常小-它只有这两个参数,一个
verbosity
参数和一个
program
参数,用于指定要提取归档文件的程序

编辑: Nizam Mohamed的回答记录了
extract\u archive
函数实际上覆盖了输出。我发现这部分是正确的-函数覆盖了ZIP文件,但不是我想要的GZ文件。对于GZ文件,函数仍然生成新文件

编辑 Padraic Cunningham的回答建议使用。因此,我下载了该代码,并用链接中的脚本替换了旧的patool库脚本。结果如下:

os.listdir()
Out[11]: ['a.gz']

patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[12]: '.'

patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[13]: '.'

patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[14]: '.'

os.listdir()
Out[15]: ['a', 'a.gz', 'a1', 'a2']

因此,
extract\u archive
函数在每次执行时都会创建新文件。在
a.gz
下存档的文件实际上与
a
有不同的名称。

如果该功能不存在,则需要添加它。例如,可以使用您自己的函数来包装该函数:

import os
from shutil import rmtree

def overwriting_extract_archive(zippath, outpath, **kwargs): 
    if os.path.exists(outpath):
        shutil.rmtree(outpath)
    patoolib.extract_archive(zippath, outdir=outpath, **kwargs)

如果您想逐个文件检查并将新输出与现有输出合并,这当然会成为一个更复杂的问题,但如果它与您描述的一样(再次运行),这应该可以工作。

如您所述,patoolib是一个通用的归档工具

使用patool可以创建、提取、测试、列出、比较、搜索和重新打包各种归档类型。patool的优点是它可以简单地处理归档文件,而无需记住大量的程序和选项

通用提取行为与特定提取行为

这里的问题是,
extract\u archive
没有公开广泛修改归档工具的基本默认行为的能力

对于.zip扩展名,patoolib将使用unzip。通过将-o作为选项传递到命令行界面,您可以获得所需的提取存档的行为。例如,
unzip-o…
,但是,这是一个特定的解压命令行选项,对于每个存档实用程序都会发生更改

例如,tar提供了覆盖选项,但没有与zip等效的缩短命令行。例如,
tar--overwrite
但是
tar-o
没有预期的效果

要解决此问题,您可以向作者发出功能请求,或使用其他库。不幸的是,patoolib的口号要求扩展所有提取实用程序函数,然后实现底层提取器自己的覆盖命令选项

对patoolib的更改示例

patoolib.programs.unzip中

def extract_zip (archive, compression, cmd, verbosity, outdir, overwrite=False):
    """Extract a ZIP archive."""
    cmdlist = [cmd]
    if verbosity > 1:
        cmdlist.append('-v')
    if overwrite:
        cmdlist.append('-o')
    cmdlist.extend(['--', archive, '-d', outdir])
    return cmdlist
patoolib.programs.tar中

def extract_tar (archive, compression, cmd, verbosity, outdir, overwrite=False):
    """Extract a TAR archive."""
    cmdlist = [cmd, '--extract']
    if overwrite:
        cmdlist.append('--overwrite')
    add_tar_opts(cmdlist, compression, verbosity)
    cmdlist.extend(["--file", archive, '--directory', outdir])
    return cmdlist
更新每个程序并不是一件小事,每个程序都是不同的

猴子补丁覆盖行为

因此,您决定不改进patoolib源代码……我们可以覆盖
extract\u archive
的行为,首先查找现有目录,将其删除,然后调用原始的
extract\u archive

您可以在您的模块中包含此代码,如果许多模块需要它,可以将其粘贴到
\uuuu init\uuuu.py

import os
import patoolib
from shutil import rmtree


def overwrite_then_extract_archive(archive, verbosity=0, outdir=None, program=None):
    if outdir:
        if os.path.exists(outdir):
            shutil.rmtree(outdir)
    patoolib.extract_archive(archive, verbosity, outdir, program)

patoolib.extract_archive = overwrite_then_extract_archive

现在,当我们调用
extract\u archive()
时,我们有了
overwrite\u然后
在提取存档文件时覆盖现有文件的功能,如果提取失败,则可能会使目标目录处于不一致的状态

如果提取失败,在提取之前删除目标目录可能会导致文件丢失

我认为最好的方法是,解压缩到临时目录并同步到目标目录

对于此解决方案,需要模块
dirsync
。但只有当
mtime
ctime
默认较新时,才需要
dirsync
snycs,而不是根据文件大小

import os
import sys
from shutil import rmtree
from patoolib import extract_archive
from dirsync import sync

archive = ''
dst_dir = ''

try:
    tmp_dir = extract_archive(archive)
except Exception as e:
    print('extract_archive error {}'.format(e))
    sys.exit(1)
else:
    try:
        sync(tmp_dir,dst_dir,'sync',options=['modtime'])
    except Exception as e:
        print('updating {} from {} failed, error {}'.format(dst_dir,tmp_dir,e))
        sys.exit(1)
    else:
        sys.exit(0)
finally:
   if os.path.exists(tmp_dir):
       rmtree(tmp_dir)
如果使用outdir传递目录,它将覆盖,包括.gz文件:

from patoolib import extract_archive

extract_archive("foo.tar.gz",verbosity=1,outdir=".")
你会看到:

patool: ... /pathto/.foo.tar.gz extracted to `.'.
它不会覆盖的唯一方法是,如果您没有传递一个目录,在该目录中,您第二次提取时会得到如下内容:

 ...foo.tar.gz extracted to `foo-1.0.2.tar1' ...(local file exists).
从bash运行时,7z每次都要求确认覆盖:

In [9]: ls
foo.gz

In [10]: from patoolib import extract_archive

In [11]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)

Processing archive: foo.gz

Extracting  foo

Everything is Ok

Size:       12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[11]: '.'

In [12]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)

Processing archive: foo.gz

file ./foo
already exists. Overwrite with 
foo?
(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename all / (Q)uit? y
Extracting  foo

Everything is Ok

Size:       12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[12]: '.'

In [13]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)

Processing archive: foo.gz

file ./foo
already exists. Overwrite with 
foo?
(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename all / (Q)uit? y
Extracting  foo

Everything is Ok

Size:       12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[13]: '.'

In [14]: ls
foo  foo.gz
正在提取tar.gz文件:

In [1]: from patoolib import extract_archive

In [2]: for x in range(4):
            extract_archive("/home/padraic/Downloads/pycrypto-2.0.1.tar.gz",verbosity=1,outdir=".")
   ...:     
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.

In [3]: ls
pycrypto-2.0.1/

同样,所有文件都会被覆盖,我能看到的唯一解释是,无论调用什么应用程序来解压
.gz
文件,默认情况下都不会覆盖或提示,但每次稍微更改名称时都会创建新文件。

似乎我找到了一个解决办法,解决了每次
提取归档文件时都会创建新文件的问题
执行
patool
库的方法。 需要强调的是,该方法能够覆盖/跳过以前为其他存档扩展提取的文件,但不能覆盖/跳过
import os,patoolib
if "name" not in os.listdir():
    patoolib.extract_archive("name.gz",outdir="C:\")