如何在不包含父目录的 Python/tar 中创建 tar.gz 存档?
Posted
技术标签:
【中文标题】如何在不包含父目录的 Python/tar 中创建 tar.gz 存档?【英文标题】:How to create tar.gz archive in Python/tar without include parent directory? 【发布时间】:2019-10-16 21:50:50 【问题描述】:我有一个包含 FolderB 和 FileB 的 FolderA。如何创建仅包含 FolderB 和 FileB 的 tar.gz 存档,删除父目录 FolderA?我正在使用 Python,并且正在 Windows 机器上运行此代码。
我找到的最佳线索是:How to create full compressed tar file using Python?
在最受好评的答案中,人们讨论了删除父目录的方法,但它们都不适合我。我试过 arcname、os.walk,并通过 subprocess.call() 运行 tar 命令。
我接近了 os.walk,但在下面的代码中,它仍然在 FolderB 和 FileB 中放置了一个“_”目录。所以,文件结构是ARCHIVE.tar.gz > ARCHIVE.tar > "_" 目录,FolderB,FileB。
def make_tarfile(output_filename, source_dir):
with tarfile.open(output_filename, "w:gz") as tar:
length = len(source_dir)
for root, dirs, files in os.walk(source_dir):
folder = root[length:] # path without "parent"
for file in files:
tar.add(os.path.join(root, folder), folder)
我使用以下方法制作存档:
make_tarfile('ARCHIVE.tar.gz', 'C:\FolderA')
我应该继续使用os.walk,还是有其他方法可以解决这个问题?
更新
Here is an image showing the contents of my archive. 如您所见,我的存档中有一个“_”文件夹,我想删除它——奇怪的是,当我提取时,只有 FolderA 和 FileB.html 显示为存档。本质上,这种行为是正确的,但如果我可以从存档中删除“_”文件夹的最后一步,那将是完美的。我将提出一个更新的问题以减少混淆。
【问题讨论】:
你为什么不能只是tar.extractall(path=destination)
,其中tar
来自tarfile.open(FolderB_path)
如果我是这个 .tar.gz 的唯一消费者,我当然可以这样做,但我正在创建一个 .tar.gz 供其他人使用,它需要具有特定的结构。
【参考方案1】:
这对我有用:
with tarfile.open(output_filename, "w:gz") as tar:
for fn in os.listdir(source_dir):
p = os.path.join(source_dir, fn)
tar.add(p, arcname=fn)
即只需列出源目录的根目录并将每个条目添加到存档中。无需遍历源目录,因为通过 tar.add() 添加目录是自动递归的。
【讨论】:
【参考方案2】:我试图提供一些示例,说明对源目录的更改如何影响最终提取的内容。
按照你的例子,我有这个文件夹结构
我有这个 python 来生成 tar 文件(来自here)
import tarfile
import os
def make_tarfile(output_filename, source_dir):
with tarfile.open(output_filename, "w:gz") as tar:
tar.add(source_dir, arcname=os.path.basename(source_dir))
tar 文件中包含哪些数据和结构取决于我作为参数提供的位置。
所以这个位置参数,
make_tarfile('folder.tar.gz','folder_A/' )
提取时会生成这个结果
如果我移动到文件夹_A 并参考文件夹_B,
make_tarfile('folder.tar.gz','folder_A/folder_B' )
这就是提取的内容,
请注意,folder_B 是此提取的根目录。
现在终于,
make_tarfile('folder.tar.gz','folder_A/folder_B/' )
将提取到这个
只有文件包含在提取中。
【讨论】:
感谢您的回复。关键是(如果我在传达这一点上做得不好,很抱歉),FolderB 和 FileB 位于同一目录级别,它们都直接位于 C:\FolderA 中。因此,您的第一个示例将提取 FolderA 及其内容;您的第二个示例将提取 FolderB 及其内容;但我希望 FolderA 的内容根本不出现 FolderA。我曾尝试使用 arcname=os.path.basename(source_dir) 但始终包含 FolderA。【参考方案3】:这是执行任务的函数。我在 Windows(使用 WinRar)上提取 tar 时遇到了一些问题,因为它似乎尝试两次提取同一个文件,但我认为正确提取存档时它会正常工作。
"""
The directory structure I have is as follows:
├───FolderA
│ │ FileB
│ │
│ └───FolderB
│ FileC
"""
import tarfile
import os
# This is where I stored FolderA on my computer
ROOT = os.path.join(os.path.dirname(__file__), "FolderA")
def make_tarfile(output_filename: str, source_dir: str) -> bool:
"""
:return: True on success, False otherwise
"""
# This is where the path to each file and folder will be saved
paths_to_tar = set()
# os.walk over the root folder ("FolderA") - note it will never get added
for dirpath, dirnames, filenames in os.walk(source_dir):
# Resolve path issues, for example for Windows
dirpath = os.path.normpath(dirpath)
# Add each folder and path in the current directory
# Probably could use zip here instead of set unions but can't be bothered to try to figure it out
paths_to_tar = paths_to_tar.union(
os.path.join(dirpath, d) for d in dirnames).union(
os.path.join(dirpath, f) for f in filenames)
try:
# This will create the tar file in the current directory
with tarfile.open(output_filename, "w:gz") as tar:
# Change the directory to treat all paths relatively
os.chdir(source_dir)
# Finally add each path using the relative path
for path in paths_to_tar:
tar.add(os.path.relpath(path, source_dir))
return True
except (tarfile.TarError, OSError) as e:
print(f"An error occurred - e")
return False
if __name__ == '__main__':
make_tarfile("tarred_files.tar.gz", ROOT)
【讨论】:
嗨,Kacper,感谢您的回复!我能够使用 7zip CLI 来实现我所需要的,但我肯定也想抽出时间尝试一下您的解决方案。我在另一个问题中发布了我的发现:***.com/questions/58423574/…【参考方案4】:您可以使用subprocess
来实现类似但much faster。
def make_tarfile(output_filename, source_dir):
subprocess.call(["tar", "-C", source_dir, "-zcvf", output_filename, "."])
【讨论】:
以上是关于如何在不包含父目录的 Python/tar 中创建 tar.gz 存档?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不传递值和 rowid 的情况下在 H base-shell 中创建表?