Wie kann ich unter Linux eine zip / tgz erstellen, sodass Windows die richtigen Dateinamen hat?

26

Codiert derzeit tar -zcf arch.tgz files/*Dateinamen in UTF, sodass Windows-Benutzer alle Zeichen in Dateinamen sehen, die nicht englisch sind , und nichts damit anfangen können.

zip -qq -r arch.zip files/* hat das gleiche Verhalten.

Wie kann ich ein zip / tgz-Archiv erstellen, damit beim Extrahieren durch Windows-Benutzer alle Dateinamen korrekt codiert werden?

kolypto
quelle

Antworten:

24

Derzeit codiert tar Dateinamen in UTF

Eigentlich codiert / decodiert tar keine Dateinamen, sondern kopiert sie einfach so wie sie sind aus dem Dateisystem. Wenn Ihr Gebietsschema UTF-8-basiert ist (wie in vielen modernen Linux-Distributionen), ist dies UTF-8. Leider ist die System-Codepage einer Windows-Box niemals UTF-8, sodass die Namen immer unkenntlich gemacht werden, außer bei Tools wie WinRAR, mit denen der verwendete Zeichensatz geändert werden kann.

Aus diesem Grund ist es nicht möglich, eine ZIP-Datei mit Nicht-ASCII-Dateinamen zu erstellen, die in verschiedenen Länderversionen von Windows und der integrierten Unterstützung für komprimierte Ordner funktionieren.

Es ist ein Nachteil der Formate tar und zip, dass es keine festen oder bereitgestellten Codierungsinformationen gibt, sodass Nicht-ASCII-Zeichen immer nicht portierbar waren. Wenn Sie ein Nicht-ASCII-Archivformat benötigen, müssen Sie eines der neueren Formate verwenden, z. B. das aktuelle 7z oder rar. Leider sind diese immer noch wackelig; In 7zip benötigen Sie den -mcuSchalter, und rar verwendet UTF-8 nur dann, wenn Zeichen erkannt werden, die nicht in der Codepage enthalten sind.

Grundsätzlich ist es ein schreckliches Durcheinander, und wenn Sie vermeiden können, Archive mit Dateinamen mit Nicht-ASCII-Zeichen zu verteilen, sind Sie viel besser dran.

Bobince
quelle
Vielen Dank! Leider wissen die meisten Benutzer nichts über 7z und rar ist proprietär :(
kolypto
Ja, das ist ein Problem. ZIP ist bei weitem die benutzerfreundlichste Lösung, da alle modernen Betriebssysteme eine nette native UI-Unterstützung haben. Leider ist das Zeichensatzproblem heutzutage in ZIP nicht wirklich lösbar (und selbst in anderen Archivformaten ist es immer noch problematisch).
Bobince
25

Hier ist ein einfaches Python-Skript, das ich geschrieben habe, um TAR-Dateien von UNIX unter Windows zu entpacken:

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()
Alexei Osipov
quelle
Genial! Mit diesem Skript konnte ich eine EUC-JP-codierte Tar-Datei konvertieren, die auf einem alten Solaris-Server erstellt wurde.
wm_eddie
Sir, Sie haben mein Leben gerettet. Gott segne dich :)
user1576772
8

Das Problem, das unter Linux die Standardeinstellung tar(GNU tar) verwendet, ist behoben ... Hinzufügen des --format=posixParameters beim Erstellen der Datei.

Zum Beispiel:
tar --format=posix -cf

In Windows verwende ich bsdtar , um die Dateien zu extrahieren .

In https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html ist geschrieben (seit 2005 !!):

> Ich habe im ChangeLog etwas über die Unterstützung von UTF-8 gelesen. Was bedeutet
das?
Ich habe keine Möglichkeit gefunden, ein Archiv zu erstellen, das
> zwischen verschiedenen Gebietsschemas austauschbar wäre .

Beim Erstellen von Archiven im POSIX.1-2001-Format (tar --format = posix oder --format = pax) konvertiert tar die Dateinamen der aktuellen Gebietsschemata in UTF-8 und speichert sie dann im Archiv. Beim Extrahieren wird der umgekehrte Vorgang ausgeführt.

PS Anstatt zu --format=posixtippen -H pax, können Sie tippen , was kürzer ist.

Sys
quelle
5

Ich glaube, Sie haben Probleme mit dem Zip-Container-Format. Teer kann unter dem gleichen Problem leiden.

Verwenden Sie stattdessen die Archivformate 7zip ( .7z) oder RAR ( .rar). Beide sind für Windows und Linux verfügbar. Die p7zipSoftware verarbeitet beide Formate.

Ich Schaffung gerade getestet .7z, .rar, .zip, und .tarDateien sowohl auf WinXP und Debian 5 und die .7zund .rarDateien speichern / wiederherstellen Dateinamen korrekt , während die .zipund .tarDateien nicht. Es spielt keine Rolle, auf welchem ​​System das Testarchiv erstellt wird.

Quacksalber
quelle
5

Ich hatte Probleme mit dem Entpacken tarund mit zipDateien, die ich von Windows-Benutzern erhalte. Während ich die Frage "Wie erstelle ich das Archiv, das funktionieren wird" nicht beantworte, helfen die folgenden Skripte beim Entpacken tarund zipkorrekten Speichern von Dateien, unabhängig vom ursprünglichen Betriebssystem.

ACHTUNG: man muss manuell die Quellencodierung abzustimmen ( cp1251, die cp866in den Beispielen unten). Befehlszeilenoptionen können in Zukunft eine gute Lösung sein.

Teer:

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

Postleitzahl:

#!/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 : Ich verwende chardetpackage, um die korrekte Codierung des unformatierten Datenblocks zu erraten. Jetzt funktioniert das Skript auf allen meinen schlechten und guten Archiven sofort.

Dinge zu beachten:

  1. Alle Dateinamen werden extrahiert und zu einer einzigen Zeichenfolge zusammengeführt, um einen größeren Teil des Texts für die Codierungs-Rate-Engine zu erhalten. Dies bedeutet, dass nur wenige Dateinamen, die auf unterschiedliche Weise verschraubt wurden, die Vermutung zerstören können.
  2. Ein spezieller schneller Pfad wurde verwendet, um einen guten Unicode-Text zu verarbeiten ( chardetfunktioniert nicht mit einem normalen Unicode-Objekt).
  3. Doctests werden hinzugefügt, um zu testen und zu demonstrieren, dass der Normalisierer jede Codierung auf relativ kurzen Zeichenfolgen erkennt.

Endgültige Version:

#!/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."
dmitry_romanov
quelle
Vielen Dank für Ihre Programme! Leider funktioniert das Zip-Programm nicht unter Python 3, aber unter Python 2.
beroal
@beroal, ich habe das Skript aktualisiert. Jetzt verwendet es die von Mozilla für Firefox entwickelte Engine, um die Codierung automatisch zu erkennen.
Dmitry_romanov
4

POSIX-1.2001 spezifizierte, wie TAR UTF-8 verwendet.

Ab 2007 wurde in der Änderungsprotokollversion 6.3.0 in PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) angegeben, wie ZIP UTF-8 verwendet.

Nur welche Tools diese Standards ordnungsgemäß unterstützen, bleibt offen.

Damjan
quelle