我在 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"
来源:
- @DozyBrat 答案有原始脚本,但它不处理文件名中的空格
- https://superuser.com/questions/1382839/zip-files-expand-with-backslashes-on-linux-no-subdirectories