如何在 Linux 中创建 zip / tgz 以便 Windows 具有正确的文件名?

kol*_*pto 26 linux zip encoding tar filenames

目前,tar -zcf arch.tgz files/*以 UTF 编码文件名,因此 Windows 用户会看到文件名损坏的所有字符,这些字符不是 english,并且无法使用它。

zip -qq -r arch.zip files/* 具有相同的行为。

如何创建 zip / tgz 存档,以便当 Windows 用户解压缩它时,所有文件名都会正确编码?

bob*_*nce 25

目前,tar 以 UTF 编码文件名

实际上 tar 根本不编码/解码文件名,它只是按原样将它们从文件系统中复制出来。如果您的语言环境是基于 UTF-8 的(如在许多现代 Linux 发行版中),那将是 UTF-8。不幸的是,Windows 机器的系统代码页永远不会是 UTF-8,因此名称将始终被修改,除非在 WinRAR 等允许更改字符集的工具上使用。

因此,不可能创建具有非 ASCII 文件名的 ZIP 文件,该文件名适用于不同国家/地区的 Windows 版本及其内置的压缩​​文件夹支持。

tar 和 zip 格式的一个缺点是没有固定或提供的编码信息,因此非 ASCII 字符总是不可移植的。如果您需要非 ASCII 存档格式,则必须使用较新的格式之一,例如最近的 7z 或 rar。不幸的是,这些仍然不稳定;在 7zip 中,您需要-mcu开关,而 rar 仍然不会使用 UTF-8,除非它检测到不在代码页中的字符。

基本上这是一个可怕的混乱,如果你能避免分发包含非 ASCII 字符的文件名的档案,你会好得多。


Ale*_*pov 25

这是我编写的一个简单的 Python 脚本,用于在 Windows 上从 UNIX 解压 tar 文件:

import tarfile

archive_name = "archive_name.tar"

def recover(name):
    return unicode(name, 'utf-8')

tar = tarfile.open(name=archive_name, mode='r', bufsize=16*1024)
updated = []
for m in tar.getmembers():
    m.name = recover(m.name)
    updated.append(m)

tar.extractall(members=updated)
tar.close()
Run Code Online (Sandbox Code Playgroud)


Sys*_*Sys 9

在 Linux 中使用默认值tar(GNU tar) 的问题已解决...--format=posix在创建文件时添加参数。

例如:
tar --format=posix -cf

在 Windows 中,为了提取文件,我使用bsdtar

https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html 中是这样写的(自2005 年以来!!):

> 我在 ChangeLog 中阅读了有关支持 UTF-8 的内容。是什么
>这意味着什么?
> 我发现无法创建可
在不同语言环境之间互换的存档。

当以 POSIX.1-2001 格式(tar --format=posix 或 --format=pax)创建档案时,tar 将文件名从当前语言环境转换为 UTF-8,然后将它们存储在档案中。提取时,进行相反的操作。

PS--format=posix您可以输入-H pax更短的 ,而不是输入。


qua*_*ote 6

我相信您遇到了 Zip 容器格式本身的问题。Tar 可能也遇到了同样的问题。

请改用7zip ( .7z) 或 RAR ( .rar) 存档格式。两者都适用于 Windows 和 Linux;该p7zip软件处理这两种格式。

我刚刚在 WinXP 和 Debian 5 上测试了创建.7z.rar.zip.tar文件,并且.7z.rar文件可以正确存储/恢复文件名,而.zip.tar文件则不能。使用哪个系统来创建测试档案并不重要。


小智 5

POSIX-1.2001 指定了 TAR 如何使用 UTF-8。

截至 2007 年,PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) 中的变更日志版本 6.3.0 指定了 ZIP 如何使用 UTF-8。

只是哪些工具能够正确支持这些标准,这仍然是一个悬而未决的问题。


dmi*_*nov 5

我在解压tarzip从 Windows 用户那里收到的文件时遇到了问题。虽然我没有回答“如何创建可以工作的存档”的问题,但无论原始操作系统是什么,下面的脚本都有助于正确解压缩tarzip文件。

警告:必须手动调整源编码(cp1251cp866在下面的示例中)。命令行选项在未来可能是一个很好的解决方案。

柏油:

#!/usr/bin/env python

import tarfile
import codecs
import sys

def recover(name):
    return codecs.decode(name, 'cp1251')

for tar_filename in sys.argv[1:]:
    tar = tarfile.open(name=tar_filename, mode='r', bufsize=16*1024)
    updated = []
    for m in tar.getmembers():
        m.name = recover(m.name)
        updated.append(m)
    tar.extractall(members=updated)
    tar.close()
Run Code Online (Sandbox Code Playgroud)

压缩:

#!/usr/bin/env python

import zipfile
import os
import codecs
import sys

def recover(name):
    return codecs.decode(name, 'cp866')

for filename in sys.argv[1:]:
    archive = zipfile.ZipFile(filename, 'r')
    infolist = archive.infolist()
    for i in infolist:
        f = recover(i.filename)
        print f
        if f.endswith("/"):
            os.makedirs(os.path.dirname(f))
        else:
            open(f, 'w').write(archive.read(i))
    archive.close()
Run Code Online (Sandbox Code Playgroud)

UPD 2018-01-02:我使用chardet包来猜测原始数据块的正确编码。现在脚本可以在我所有的坏档案和好的档案上开箱即用。

注意事项:

  1. 所有文件名都被提取并合并到单个字符串中,为编码猜测引擎制作更大的文本片段。这意味着很少有文件名以不同的方式被搞砸,每个文件名可能会破坏猜测。
  2. 特殊的快速路径用于处理好的 unicode 文本(chardet不适用于普通的 unicode 对象)。
  3. 添加 Doctests 以测试和证明规范化器识别合理短字符串上的任何编码。

最终版本:

#!/usr/bin/env python2
# coding=utf-8

import zipfile
import os
import codecs
import sys

import chardet


def make_encoding_normalizer(txt):
    u'''
    Takes raw data and returns function to normalize encoding of the data.
        * `txt` is either unicode or raw bytes;
        * `chardet` library is used to guess the correct encoding.

    >>> n_unicode = make_encoding_normalizer(u"??????!")
    >>> print n_unicode(u"???? ??????")
    ???? ??????

    >>> n_cp1251 = make_encoding_normalizer(u"??????!".encode('cp1251'))
    >>> print n_cp1251(u"???? ??????".encode('cp1251'))
    ???? ??????
    >>> type(n_cp1251(u"???? ??????".encode('cp1251')))
    <type 'unicode'>
    '''
    if isinstance(txt, unicode):
        return lambda text: text

    enc = chardet.detect(txt)['encoding']
    return lambda file_name: codecs.decode(file_name, enc)


for filename in sys.argv[1:]:
    archive = zipfile.ZipFile(filename, 'r')
    infolist = archive.infolist()

    probe_txt = "\n".join(i.filename for i in infolist)
    normalizer = make_encoding_normalizer(probe_txt)

    for i in infolist:
        print i.filename
        f = normalizer(i.filename)
        print f
        dirname = os.path.dirname(f)
        if dirname:
            assert os.path.abspath(dirname).startswith(os.path.abspath(".")), \
                "Security violation"
            if not os.path.exists(dirname):
                os.makedirs(dirname)
        if not f.endswith("/"):
            open(f, 'w').write(archive.read(i))
    archive.close()


if __name__ == '__main__' and len(sys.argv) == 1:
    # Hack for Python 2.x to support unicode source files as doctest sources.
    reload(sys)
    sys.setdefaultencoding("UTF-8")

    import doctest
    doctest.testmod()

    print "If there are no messages above, the script passes all tests."
Run Code Online (Sandbox Code Playgroud)