有一些库用于通过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
具有不同的名称。
答案 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
仅在mtime
和ctime
默认情况下更新时才会进行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:\")
这似乎解决了我的问题。