将 Windows 创建的 ZIP 转换为 Linux(内部路径问题)

将 Windows 创建的 ZIP 转换为 Linux(内部路径问题)

我在 Windows 计算机上创建了一个 .zip(不在我的控制范围内)。 zip 文件包含解压缩时需要保留的路径。

但是,当我解压缩时,所有文件最终都会像:
unzip_dir/\window\path\separator\myfile.ext

我都尝试过,有选项和没有-j选项。我的问题是我需要下的该路径信息\window\path\separator\。我需要在解压缩时创建该文件结构。

我可以在脚本中轻松地mv创建文件并翻转到\/但随后会出现目标路径目录不存在的错误。我现在的解决方法是mkdir -p路径(转换\为后/),然后将cp文件转移到这些路径。

但是文件很多,mkdir -p每个文件的这些冗余语句确实会减慢速度。

有没有更优雅的方法将 Windows 路径的 zip 文件转换为 Linux 路径?

答案1

用于7z rn重命名存档中的文件,以便它们具有正斜杠。然后,当您提取存档时,将创建目录。

要重命名文件,请列出存档中包含斜杠的文件的路径,并生成替换字符串列表,使用awk例如将反斜杠更改为斜杠。

7z rn windows.zip $(7z l windows.zip | grep '\\' | awk '{ print $6, gensub(/\\/, "/", "g", $6); }' | paste -s)

答案2

我认为创建 zip 文件时出了问题,因为当我在 Windows 上创建 zip 文件时有(可移植的)正斜杠:

zip.exe -r pip pip
updating: pip/ (244 bytes security) (stored 0%)
  adding: pip/pip.log (164 bytes security) (deflated 66%)

但是现在您拥有文件名包含带反斜杠的“路径”的文件,您可以在以下位置运行以下程序unzip_dir

#! /usr/bin/env python

# already created directories, walk works topdown, so a child dir
# never creates a directory if there is a parent dir with a file.
made_dirs = set()

for root, dir_names, file_names in os.walk('.'):
    for file_name in file_names:
        if '\\' not in file_name:
            continue
        alt_file_name = file_name.replace('\\', '/')
        if alt_file_name.startswith('/'):
            alt_file_name = alt_file_name[1:]  # cut of starting dir separator
        alt_dir_name, alt_base_name = alt_file_name.rsplit('/', 1)
        print 'alt_dir', alt_dir_name
        full_dir_name = os.path.join(root, alt_dir_name)
        if full_dir_name not in made_dirs:
            os.makedirs(full_dir_name)  # only create if not done yet
            made_dirs.add(full_dir_name)
        os.rename(os.path.join(root, file_name),
                  os.path.join(root, alt_file_name))

这会处理程序启动目录下任何目录中的文件。鉴于您描述的问题,unzip_dir可能没有任何子目录可供启动,并且程序只能遍历当前目录中的文件。

答案3

这只是 @anton 答案的更新,其中包括 @madmuffin 的修复(FileExistsError: [Errno 17] File exists以及缺少的os模块导入)、Python 3 的修复 ( SyntaxError: Missing parentheses in call to 'print') 以及缺少的模块导入的修复errno( NameError: name 'errno' is not defined)。

#! /usr/bin/env python

import os
import errno

# already created directories, walk works topdown, so a child dir
# never creates a directory if there is a parent dir with a file.
made_dirs = set()

for root, dir_names, file_names in os.walk('.'):
    for file_name in file_names:
        if '\\' not in file_name:
            continue
        alt_file_name = file_name.replace('\\', '/')
        if alt_file_name.startswith('/'):
            alt_file_name = alt_file_name[1:]  # cut of starting dir separator
        alt_dir_name, alt_base_name = alt_file_name.rsplit('/', 1)
        print('alt_dir', alt_dir_name)
        full_dir_name = os.path.join(root, alt_dir_name)
        if full_dir_name not in made_dirs:
            try:
                os.makedirs(full_dir_name)
            except OSError as exc:
                if exc.errno == errno.EEXIST and os.path.isdir(full_dir_name):
                    # the pass already exists and is a folder, let's just ignore it
                    pass
                else:
                    raise 
            made_dirs.add(full_dir_name)
        os.rename(os.path.join(root, file_name),
                  os.path.join(root, alt_file_name))

答案4

标准明确规定所有斜杠必须是正斜杠

https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

4.4.17.1 The name of the file, with optional relative path.
   The path stored MUST NOT contain a drive or
   device letter, or a leading slash.  All slashes
   MUST be forward slashes '/' as opposed to
   backwards slashes '\' for compatibility with Amiga
   and UNIX file systems etc.  If input came from standard
   input, there is no file name field.
   

这是一个修复非投诉 zip 文件的脚本,它还处理空格,这是之前答案中缺少的:

#!/bin/sh
CMD_INSTRUCTIONS=$(7z l -ba -slt "$1" | grep '\\' | sed 's/^Path = //g' | sed 's/.*/"&"/' | gawk '{ print $0, gensub(/\\/, "/", "g", $0); }' | sed 's|\\|\\\\|g' | paste -s -)
CMD_7Z="7z rn \"$1\" $CMD_INSTRUCTIONS"
eval "$CMD_7Z"

来源:

相关内容