Como posso criar um zip / tgz no Linux, de modo que o Windows tenha nomes de arquivos apropriados?

26

Atualmente, tar -zcf arch.tgz files/* codifica nomes de arquivos em UTF, de modo que os usuários do Windows veem todos os caracteres estragados em nomes de arquivos que não são em inglês e não podem fazer nada com eles.

zip -qq -r arch.zip files/* tem o mesmo comportamento.

Como posso criar um arquivo zip / tgz para que, quando os usuários do Windows o extraírem, ele tenha todos os nomes de arquivos codificados corretamente?

    
por kolypto 25.10.2009 / 15:41

6 respostas

24

Currently, tar encodes filenames in UTF

Na verdade, o tar não codifica / decodifica nomes de arquivos, simplesmente copia-os do sistema de arquivos como estão. Se sua localidade for baseada em UTF-8 (como em muitas distribuições Linux modernas), isso será UTF-8. Infelizmente, a página de códigos do sistema de uma caixa do Windows nunca é UTF-8, portanto, os nomes sempre serão desconfigurados, exceto em ferramentas como o WinRAR, que permitem que o conjunto de caracteres seja alterado.

Portanto, é impossível criar um arquivo ZIP com nomes de arquivos não-ASCII que funcionem em versões diferentes do Windows e seu suporte a pastas compactadas embutidas.

É uma lacuna dos formatos tar e zip que não há informações de codificação fixas ou fornecidas, de modo que os caracteres não-ASCII sempre serão não portáteis. Se você precisa de um formato de arquivo não-ASCII, você terá que usar um dos formatos mais recentes, como o recente 7z ou o rar. Infelizmente, estes ainda são instáveis; no 7zip você precisa da opção -mcu , e o rar ainda não usará o UTF-8, a menos que detecte caracteres que não estejam na página de códigos.

Basicamente, é uma bagunça horrível e se você puder evitar a distribuição de arquivos contendo nomes de arquivos com caracteres não-ASCII, você estará bem melhor.

    
por 26.10.2009 / 00:24
25

Aqui está um script Python simples que escrevi para descompactar arquivos tar do UNIX no Windows:

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()
    
por 20.09.2010 / 20:56
8

O problema, usando no Linux o padrão tar (GNU tar), é resolvido ... adicionando o parâmetro --format=posix ao criar o arquivo.

Por exemplo: tar --format=posix -cf

No Windows, para extrair os arquivos, eu uso bsdtar .

No link está escrito (desde 2005 !!):

> I read something in the ChangeLog about UTF-8 being supported. What does
> this mean?
> I found no way to create an archive that would be interchangeable
> between different locales.

When creating archives in POSIX.1-2001 format (tar --format=posix or --format=pax), tar converts file names from the current locales to UTF-8 and then stores them in archive. When extracting, the reverse operation is performed.

P.S. Em vez de digitar --format=posix , você pode digitar -H pax , que é menor.

    
por 13.10.2012 / 15:51
5

Acredito que você esteja com problemas com o próprio formato do contêiner Zip. O alcatrão pode estar sofrendo do mesmo problema.

Use os formatos de arquivo 7zip ( .7z ) ou RAR ( .rar ). Ambos estão disponíveis para Windows e Linux; o software p7zip lida com ambos os formatos.

Acabei de testar a criação de arquivos .7z , .rar , .zip e .tar no WinXP e no Debian 5, e os arquivos .7z e .rar armazenam / restauram nomes de arquivos corretamente enquanto o .zip e .tar arquivos não. Não importa qual sistema é usado para criar o arquivo de teste.

    
por 25.10.2009 / 18:14
5

Tive problemas com a descompactação de arquivos tar e zip que recebo de usuários do Windows. Embora eu não responda à pergunta "como criar o arquivo que funcionará", os scripts abaixo ajudam a descompactar corretamente os arquivos tar e zip , independentemente do sistema operacional original.

AVISO: é preciso ajustar a codificação de origem manualmente ( cp1251 , cp866 nos exemplos abaixo). Opções de linha de comando podem ser uma boa solução em um futuro.

Alcatrão:

#!/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()

CEP:

#!/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()

UPD 2018-01-02 : eu uso o pacote chardet para adivinhar a codificação correta do fragmento bruto de dados. Agora script funciona fora da caixa em todos os meus arquivos ruins, bem como uns bons.

Coisas a serem observadas:

  1. Todos os nomes de arquivos são extraídos e mesclados na sequência única para criar uma parte maior do texto do mecanismo de adivinhação de codificação. Isso significa que poucos nomes de arquivo podem ser estragados de maneira diferente, cada um pode estragar o palpite.
  2. O atalho rápido especial foi usado para manipular um bom texto unicode ( chardet não funciona com um objeto unicode normal).
  3. Doctests são adicionados para testar e demonstrar que o normalizador reconhece qualquer codificação em strings razoavelmente curtas.

Versão final:

#!/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."
    
por 07.04.2014 / 07:41
4

POSIX-1.2001 especificou como o TAR usa o UTF-8.

A partir de 2007, a versão 6.3.0 do changelog no PKZIP APPNOTE.TXT ( link ) especificado como o ZIP usa o UTF-8.

São apenas as ferramentas que suportam esses padrões adequadamente, o que permanece uma questão em aberto.

    
por 16.12.2011 / 06:06