问题 覆盖以前提取的文件而不是创建新文件
有一些库用于通过Python提取存档文件,例如gzip,zipfile库,rarfile,tarfile,patool等。我发现其中一个库(patool)特别有用,因为它具有交叉格式功能它可以提取几乎任何类型的存档,包括最流行的存档,如ZIP,GZIP,TAR和RAR。
要使用patool提取存档文件,它就像这样简单:
patoolib.extract_archive( "Archive.zip",outdir="Folder1")
在哪里 "Archive.zip"
是存档文件的路径和 "Folder1"
是存储解压缩文件的目录的路径。
提取工作正常。问题是,如果我再次为完全相同的存档文件运行相同的代码,则相同的提取文件将存储在同一文件夹中,但名称略有不同(第一次运行时为filename,第二次为filename1,文件名为11)第三等等。
如果目录中已经存在同名文件,我需要代码来覆盖提取的文件。
这个 extract_archive
函数看起来很小 - 它只有这两个参数,a verbosity
参数,和 program
参数,指定要用于提取存档的程序。
编辑:
Nizam Mohamed的回答记录了这一点 extract_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_archive
函数每次执行时都会创建新文件。该文件存档在 a.gz
有一个不同的名字 a
其实。
2371
2018-04-14 15:45
起源
答案:
如您所述,patoolib旨在成为通用归档工具。
可以使用patool创建,提取,测试,列出,比较,搜索和重新打包各种存档类型。 patool的优点是处理存档文件的简单性,而无需记住无数的程序和选项。
通用提取行为与特定提取行为
这里的问题是 extract_archive
没有公开广泛修改存档工具的基础默认行为的能力。
对于.zip扩展名,patoolib将使用解压缩。通过将-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_archive
最初查找现有目录,删除它,然后调用原始目录 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()
我们有的功能 overwrite_then_extract_archive()
。
4
2018-04-16 18:49
如果该功能不存在,则需要添加该功能。这方面的一个例子是用你自己的函数包装函数:
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
2018-04-16 18:44
使用 主要来源 如果使用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
默认情况下,文件不会覆盖或提示,但每次稍微更改名称时都会创建新文件。
2
2018-04-22 14:03
如果提取失败,则在提取存档时覆盖现有文件可能会使目标目录处于不一致状态。
如果提取失败,在提取之前删除目标目录可能会导致文件丢失。
我认为最好的方法是,提取到临时目录并同步到目标目录。
对于此解决方案,模块 dirsync
是必须的。但 dirsync
只有当 mtime
和 ctime
默认情况下更新,而不是文件大小。
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)
2
2018-04-21 20:56
似乎我找到了解决每次创建新文件的问题 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:\")
这似乎解决了我的问题。
1
2018-04-27 11:26