Python 的 shutil.make_archive() 在 Linux 上创建点目录(当使用 tar 或 gztar 时)

Python's shutil.make_archive() creates dot directory on Linux (when using tar or gztar)

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

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

生成的存档文件不只是存储目录 X 的内容,而是创建一个 .存档中的文件夹(文件夹 directoryX 的内容存储在此 . 文件夹中)。

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

使用 python 版本 -> 3.8.10

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

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

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

  1. 是我们tar从中归档的目录(在 chdiring 到 root_dir 之后)
  2. 默认到当前目录(具体来说,os.curdir

os.curdir 实际上是一个常量字符串,'.',并且与 tar 命令行实用程序匹配,shutil.make_archive(和 tar.add 它实现的就) 存储“给定”的完整路径(在本例中,'./' 加上文件相对路径的其余部分)。如果你 运行 tar -c -z -C directoryX -f NameOfArchive.tar.gz .,你最终会得到一个 tar 球,其中也充满了 ./ 前缀文件(-C directoryX 与 [=17 做同样的事情=],并且 . 参数与默认值 base_dir='.') 相同。

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

需要说明的是,这种行为应该没问题;一个名为 ./foo 的 tar 条目和一个名为 foo 的条目在大多数情况下是等效的。如果实在嫌麻烦,可以直接改用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(): 块中来简单地排除符号链接,或者省略特定目录的递归添加通过有条件地将 recursive=False 设置为 tar.add 对您不想包含其内容的目录的调用;您甚至可以为 tar.add 有条件地调用提供一个 filter 函数即使涉及深度递归,也排除特定条目。