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

时间:2015-04-14 15:45:59

标签: python file extract overwrite ziparchive

有一些库用于通过Python提取存档文件,例如gzip,zipfile库,rarfile,tarfile,patool等。由于其交叉格式功能,我发现其中一个库(patool)特别有用从某种意义上说,它可以提取几乎任何类型的档案,包括最流行的档案,如ZIP,GZIP,TAR和RAR。

要使用patool提取存档文件,它就像这样简单:

patoolib.extract_archive( "Archive.zip",outdir="Folder1")

"Archive.zip"是存档文件的路径,"Folder1"是存储解压缩文件的目录的路径。

提取工作正常。问题是,如果我为完全相同的存档文件再次运行相同的代码,则相同的提取文件将存储在同一文件夹中,但名称略有不同(第一次运行时为文件名,第二次运行时为filename1,文件名为11)第三,等等。

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

这个extract_archive函数看起来很小 - 它只有这两个参数,一个verbosity参数和一个program参数,它指定了你想要提取档案的程序。

编辑: Nizam Mohamed的回答记录了extract_archive函数实际上覆盖了输出。我发现这部分是正确的 - 该函数会覆盖ZIP文件,但不会覆盖我所追求的GZ文件。对于GZ文件,该函数仍会生成新文件。

编辑 Padraic Cunningham的回答建议使用master source。因此,我下载了该代码,并用链接中的脚本替换了旧的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_archive函数每次执行时都会创建新文件。在a.gz下归档的文件实际上与a具有不同的名称。

5 个答案:

答案 0 :(得分:4)

正如您所说,patoolib旨在成为通用归档工具。

  

可以使用patool创建,提取,测试,列出,比较,搜索和重新打包各种存档类型。 patool的优点是处理存档文件的简单性,而不必记住无数的程序和选项。

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

这里的问题是extract_archive没有公开广泛修改存档工具的基础默认行为的能力。

对于.zip扩展名,patoolib将使用解压缩。通过将-o作为选项传递给命令行界面,可以获得所需的提取存档的行为。 ie 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_archive的行为,以便最初查找现有目录,删除它,然后调用原始{{1} }。

您可以在模块中包含此代码,如果许多模块需要它,可能会粘贴它extract_archive

__init__.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_archive()

答案 1 :(得分:2)

如果该功能不存在,则需要添加该功能。这方面的一个例子是用你自己的函数包装函数:

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)

如果你想逐个文件检查并将新输出与现有输出合并,那当然会成为一个更复杂的问题,但如果它就像你描述的那样(第二次运行它),这应该有效。

答案 2 :(得分:2)

如果提取失败,在提取存档时覆盖现有文件可能会使目标目录处于不一致状态。

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

我认为最好的方法是,提取到临时目录并同步到目标目录。

对于此解决方案,需要模块dirsync。但dirsync仅在mtimectime默认情况下更新时才会进行snyc,而不是文件大小。

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)

答案 3 :(得分:2)

如果使用outdir传递目录,则使用master source将覆盖包括 .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文件都不会覆盖或提示,但每次稍微更改名称时都会创建新文件。

答案 4 :(得分:1)

我似乎找到了每次执行extract_archive库的patool方法时创建新文件的问题的解决方法。 需要强调的是,该方法能够覆盖/跳过先前为其他存档扩展提取的文件,但不能覆盖Gun Zipped文件。

我注意到,当提取任何Gun Zipped文件(.gz)时,提取的文件与存档具有相同的名称,但没有任何扩展名。为了更好地说明这一点,如果您将名称从X.gz更改为Y.gz,然后提取存档,则解压缩的文件将具有名称" Y" 。 因此,我能够实现一个简单的条件:

import os,patoolib
if "name" not in os.listdir():
    patoolib.extract_archive("name.gz",outdir="C:\")

这似乎解决了我的问题。