web-dev-qa-db-ja.com

LinuxでZip / tgzを作成して、Windowsが適切なファイル名を持つようにするにはどうすればよいですか?

現在、 tar -zcf Arch.tgz files/*は、ファイル名をUTFでエンコードするため、Windowsユーザーは、ファイル名に書きこまれたすべての文字英語ではないを表示し、それを使用して何もできません。

Zip -qq -r Arch.Zip files/*も同じ動作です。

Windowsユーザーが解凍したときにすべてのファイル名が適切にエンコードされるように、Zip/tgzアーカイブを作成するにはどうすればよいですか?

26
kolypto

現在、tarはファイル名をUTFでエンコードします

実際のところ、tarはファイル名をまったくエンコード/デコードしません。ファイルシステムからそのままコピーするだけです。ロケールがUTF-8ベースの場合(最近の多くのLinuxディストリビューションなど)、それはUTF-8になります。残念ながら、Windowsボックスのシステムコードページは決してUTF-8ではないため、WinRARなどの文字セットの変更を許可するツールを除いて、名前は常にマングルされます。

そのため、さまざまな国のWindowsのリリースとその組み込みの圧縮フォルダーサポートで機能する非ASCIIファイル名でZipファイルを作成することは不可能です。

固定または提供されたエンコード情報がないのはtarおよびZip形式の欠点であるため、非ASCII文字は常に移植できません。 ASCII以外のアーカイブ形式が必要な場合は、最近の7zやrarなどの新しい形式のいずれかを使用する必要があります。残念ながら、これらはまだ不安定です。 7Zipでは-mcuスイッチ。コードページにない文字を検出しない限り、rarはUTF-8を使用しません。

基本的にそれは恐ろしい混乱であり、ASCII以外の文字を含むファイル名を含むアーカイブの配布を避けることができれば、はるかに良いでしょう。

24
bobince

以下は、Windows上のUNIXからtarファイルを解凍するために記述した簡単なPythonスクリプトです。

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()
25
Alexei Osipov

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!!):

>サポートされているUTF-8について、ChangeLogで何かを読みました。なに
>これはどういう意味ですか?
>互換性のあるアーカイブを作成する方法が見つからなかった
>異なるロケール間。

POSIX.1-2001形式(tar --format = posixまたは--format = pax)でアーカイブを作成する場合、tarはファイル名を現在のロケールからUTF-8に変換してからアーカイブに保存します。抽出時は逆の操作を行います。

追伸--format=posixと入力する代わりに、短い[-H pax]と入力できます。

9
Sys

Zipコンテナー形式自体に問題が発生していると思います。タールも同じ問題に苦しんでいる可能性があります。

代わりに 7Zip.7z)またはRAR(.rar)アーカイブ形式を使用してください。どちらもWindowsとLinuxで使用できます。 p7Zipソフトウェアは両方の形式を処理します。

WinXPとDebian 5の両方で.7z.rar.Zip.tarファイル、および.7z.rarファイルの作成をテストしました.Zipファイルと.tarファイルにはないファイル名を正しく保存/復元します。テストアーカイブの作成にどのシステムを使用するかは関係ありません。

5
quack quixote

Windowsユーザーから受け取ったtarおよびZipファイルの解凍に問題がありました。 「動作するアーカイブを作成する方法」という質問には答えませんが、以下のスクリプトは、元のOSに関係なくtarおよびZipファイルを正しく解凍するのに役立ちます。

警告:ソースエンコーディングを手動で調整する必要があります(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()

郵便番号:

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

PD 2018-01-02chardetパッケージを使用して、データの生のチャンクの正しいエンコーディングを推測します。これで、スクリプトは、問題のないアーカイブだけでなく、優れたアーカイブでもそのまま動作します。

注意事項:

  1. すべてのファイル名が抽出され、単一の文字列にマージされて、エンコーディング推測エンジンのテキストのより大きな部分が作成されます。それは、それぞれが異なる方法でねじ込まれたいくつかのファイル名が推測を台無しにする可能性があることを意味します。
  2. 特別な高速パスを使用して適切なUnicodeテキストを処理しました(chardetは通常のUnicodeオブジェクトでは機能しません)。
  3. Doctestが追加され、テストと、ノーマライザーが適度に短い文字列のエンコーディングを認識することを示します。

最終版:

#!/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."
5
dmitry_romanov

POSIX-1.2001は、TARがUTF-8を使用する方法を指定しました。

2007年現在、PKZIP APPNOTE.TXT( http://www.pkware.com/documents/casestudies/APPNOTE.TXT )のchangelogバージョン6.3.0は、ZipがUTF-8を使用する方法を指定しています。

これらの標準を適切にサポートするのはこのツールだけであり、未解決の問題です。

4
damjan