【问题标题】:Python's shutil.make_archive() creates dot directory on Linux (when using tar or gztar)Python shutil.make_archive() 在 Linux 上创建点目录(使用 tar 或 gz tar 时)
【发布时间】:2022-01-01 22:31:54
【问题描述】:

我正在使用一个基本的 Python 脚本来创建一个包含目录“directoryX”内容的存档:

shutil.make_archive('NameOfArchive', format='gztar', root_dir=getcwd()+'/directoryX/')

生成的存档不仅仅是存储 directoryX 的内容,而是创建一个 .存档中的文件夹(并且文件夹 directoryX 的内容存储在此 . 文件夹中)。

有趣的是,这只会发生在 .tar 和 tar.gz 上,而不会发生在 .zip 上

使用的python版本-> 3.8.10

似乎在使用 .tar 或 .tar.gz 格式时,“./”的默认 base_dir 会被逐字接受,并会创建一个名为“.”的文件夹。 我尝试使用 base_dir=os.currdir 但得到了相同的结果...... 尝试也使用python2但得到了相同的结果。

这是 shutil.make_archive 的错误还是我做错了什么?

【问题讨论】:

    标签: python shutil


    【解决方案1】:

    这是一种记录在案的行为,有点奇怪。 make_archivebase_dir 参数记录到:

    1. 成为我们开始归档的目录(在chdiring 到root_dir 之后)
    2. 默认为当前目录(具体来说,os.curdir

    os.curdir 实际上是一个常量字符串'.',并且与tar 命令行实用程序匹配,shutil.make_archive(以及它的实现方式tar.add)存储完整路径“给定”(在这种情况下,'./' 加上文件的其余相对路径)。如果你运行tar -c -z -C directoryX -f NameOfArchive.tar.gz .,你最终也会得到一个包含./前缀文件的压缩包(-C directoryXroot_dir做同样的事情,.参数与默认@987654337相同@)。

    我没有看到一个简单的解决方法可以保留shutil.make_archive 的简单性;如果你尝试传递 base_dir='',它会在尝试传递 stat '' 时死掉,所以就不行了。

    要清楚,这种行为应该没问题;在大多数情况下,一个名为 ./foo 的 tar 条目和一个名为 foo 的 tar 条目是等效的。如果实在困扰你,你可以直接切换到使用tarfile模块,例如:

    # Imports at top of file
    import os
    import tarfile
    
    # Actual code
    with tarfile.open('NameOfArchive.tar.gz', 'w:gz') as tar:
        for entry in os.scandir('directoryX'):
            # Operates recursively on any directories, using the arcname as the base,
            # so you add the whole tree just by adding all the entries in the top
            # level directory. Using arcname of entry.name means it's equivalent to
            # adding os.path.basename(entry.path), omitting all directory components
            tar.add(entry.path, arcname=entry.name)
    
        # The whole loop *could* be replaced with just:
        # tar.add('directoryX', arcname='')
        # which would add all contents recursively, but it would also put an entry
        # for '/' in, which is undesirable
    

    对于像这样的目录结构:

    directoryX/
      |
      \- foo
      \- bar
      \- subdir/
           |
           \- spam
           \- eggs
    

    tar 的结果将是:

    foo
    bar
    subdir/
    subdir/eggs
    subdir/spam
    

    对比该:

    ./foo
    ./bar
    ./subdir/
    ./subdir/eggs
    ./subdir/spam
    

    您当前的代码生成。

    代码工作量稍微增加了一点,但没有那么更糟;两个导入和三行代码,并且可以更好地控制添加的内容(例如,您可以通过将 tar.add 调用包装在 if not entry.is_symlink(): 块中来轻松排除符号链接,或者通过有条件地设置 @ 来省略特定目录的递归添加987654352@ 到 tar.add 调用您不想包含其内容的目录;您甚至可以为 tar.add 调用提供 filter 函数,以有条件地排除特定条目,即使涉及深度递归)。

    【讨论】:

    • 谢谢你,我将只使用 tarfile,因为我有另一个脚本,它只是在存档中查找文件并且不希望在其中有另一个文件夹(我不想编辑这个脚本,因为它是一些其他遗留脚本所必需的)。我有一个问题,为什么这只发生在 .tar 和 .gztar 上?如果我使用 zip 作为格式,则不会发生此问题。
    • @iTsYaBoiii:看起来shutil 实用函数实现了make_archive 用于压缩文件使用os.path.normpath,它去除了./ 前缀。即使没有,zipfile.ZipInfo.from_file(这是从磁盘添加文件的方式)在内部使用os.path.normpath,所以无论它如何使用,都会删除前导./。我不确定zip格式是否允许以./开头的相对路径名,所以它可能这样做是为了避免与规范冲突(tar本身很容易用这样的路径创建档案,而Python匹配行为)。
    猜你喜欢
    • 2017-04-09
    • 2017-05-15
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2019-04-15
    • 1970-01-01
    • 2010-11-10
    相关资源
    最近更新 更多