Python 覆盖以前提取的文件而不是创建新文件
有几个库用于通过Python提取归档文件,例如gzip、zipfile库、rarfile、tarfile、patool等。我发现其中一个库(patool)特别有用,因为它具有跨格式特性,可以提取几乎任何类型的归档文件,包括最流行的ZIP、gzip、,焦油和稀薄油 要使用patool提取存档文件,非常简单: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”是存储
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:\")