bash - 压缩文件——路径名中的撇号
问题描述
我无法压缩文件夹名称中带有撇号的任何文件。例如
zip -rmT "./I've Been Everywhere (Today)/PDFs.zip" "./PDFDirectory"
或者
zip -rmT ./I\'ve\ Been\ Everywhere\ \(Today\)/PDFs.zip ./PDFDirectory
我尝试了各种引用、转义和使用变量而不是输入字符串的方式,但仍然得到
sh: 1: Syntax error: "(" unexpected
test of I've Been Everywhere (Today)/PDFs.zip FAILED
这发生在命令行和 bash 脚本中。从路径中删除撇号/单引号可以清除错误。Zip 无法处理路径名中的撇号还是我遗漏了什么?
更新 09/22/21 1443 EDT
Marco 是正确的,它是导致错误的 -T (--test)。由于我需要该测试,因此我目前正在通过 cd 进入文件夹并在压缩完成后 cd 回解决该问题(YUCK!)。
解决方案
似乎-T
( --test
) 选项不能很好地处理'
文件路径中的单引号。如果您在没有它的情况下运行命令,它应该可以正常工作,但您将失去完整性检查。更好的解决方法是在当前目录中创建 zip 文件,然后再将其移动到
I've Been Everywhere (Today)
目录中,例如:
$ zip -rmT PDFs.zip PDFDirectory && mv PDFs.zip "I've Been Everywhere (Today)"
adding: PDFDirectory/ (stored 0%)
adding: PDFDirectory/bar.pdf (stored 0%)
adding: PDFDirectory/baz.pdf (stored 0%)
adding: PDFDirectory/foo.pdf (stored 0%)
test of PDFs.zip OK
$ ls I\'ve\ Been\ Everywhere\ \(Today\)/
PDFs.zip
$
(参见最后使用-b
选项的另一种解决方法)
只是为了好玩,我试图查看zip
程序源代码以(尝试)了解发生了什么(这里是 github 镜像)。
如前所述,问题仅发生在(我认为)-T
对 zip 文件进行完整性检查的选项中。来自man zip
:
-T
--测试测试新 zip 文件的完整性。如果检查失败,旧的 zip 文件将保持不变,并且(使用 -m 选项)不会删除任何输入文件。
-TT cmd
--解压缩命令 cmd当使用 -T 选项时,使用命令 cmd 而不是“unzip -tqq”来测试存档。在 Unix 上,要使用当前目录中的解压缩副本而不是标准系统解压缩,可以使用:
zip archive file1 file2 -T -TT "./unzip -tqq"
在 cmd 中,{} 替换为临时存档的名称,否则将存档的名称附加到命令的末尾。检查返回码是否成功(Unix 上为 0)。
使用该-T
选项时,在 zip 文件上zip
运行。unzip -tqq
我在 中找到了实际执行此操作的一段代码,zip.c
函数是:
local void check_zipfile(zipname, zippath)
char *zipname;
char *zippath;
/* Invoke unzip -t on the given zip file */
它很长而且真的很“#ifdef-heavy”,我假设你在一个类似unix的系统上,我只会报告相关部分:
if ((cmd = malloc(20 + strlen(zipname))) == NULL) {
ziperr(ZE_MEM, "building command string for testing archive");
}
strcpy(cmd, "unzip -t ");
...
if (!verbose) strcat(cmd, "-qq ");
if (check_unzip_version("unzip") == 0)
ZIPERR(ZE_TEST, zipfile);
# ifdef UNIX
strcat(cmd, "'"); /* accept space or $ in name */
strcat(cmd, zipname);
strcat(cmd, "'");
...
}
result = system(cmd);
...
free(cmd);
cmd = NULL;
if (result) {
...
fprintf(mesg, "test of %s FAILED\n", zipfile);
ziperr(ZE_TEST, "original files unmodified");
}
该代码基本上构建了一个命令字符串cmd
并使用
system()
函数运行它。system()
是一个 C 库函数,它:
使用 fork(2) 创建一个子进程,该子进程执行 command using execl(3) 中指定的 shell 命令,如下所示:
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
system() 在命令完成后返回。
执行的命令check_zipfile()
实际上是:
/bin/sh -c "unzip -t -qq 'I've Been Everywhere (Today)/PDFs.zip'"
注意'
文件路径周围的两个添加了这些行:
strcat(cmd, "'"); /* accept space or $ in name */
strcat(cmd, zipname);
strcat(cmd, "'");
如果zipname
包含一个'
(如您的情况)shell命令将被“破坏”,因为'
添加了两个,不要再“包装”完整的文件路径。如果我尝试运行该命令,我会得到相同的错误报告zip
,即 shell 语法错误:
$ sh -c "unzip -t -qq 'I've Been Everywhere (Today)/PDFs.zip'"
sh: 1: Syntax error: "(" unexpected
$
所以该-T
选项运行格式错误的 shell 命令!
奇怪的是,如果你的文件路径不包括目录路径,即你只传递一个文件名,它可以包含'
没有任何问题,例如:
$ zip -rmT "foo's bar.zip" PDFDirectory
updating: PDFDirectory/ (stored 0%)
updating: PDFDirectory/bar.pdf (stored 0%)
updating: PDFDirectory/baz.pdf (stored 0%)
updating: PDFDirectory/foo.pdf (stored 0%)
test of foo's bar.zip OK
$
我认为这是因为zip
为“压缩”创建了一个临时文件,并且仅在最后将其重命名为提供的名称。
查看此临时文件如何工作的代码,我能够使用-b
( --temp-path
) 选项找到该问题的另一种解决方法:
$ zip -rmT -b /tmp "I've Been Everywhere (Today)/PDFs.zip" PDFDirectory
adding: PDFDirectory/ (stored 0%)
adding: PDFDirectory/bar.pdf (stored 0%)
adding: PDFDirectory/baz.pdf (stored 0%)
adding: PDFDirectory/foo.pdf (stored 0%)
test of I've Been Everywhere (Today)/PDFs.zip OK
$ ls I\'ve\ Been\ Everywhere\ \(Today\)/
PDFs.zip
$
使用-b
,zip
将执行命令:
/bin/sh -c "unzip -t -qq '/tmp/ziXXXXXX'"
对于-T
格式正确的 shell 命令的选项,最后,它将重命名/tmp/ziXXXXXX
为I've Been Everywhere (Today)/PDFs.zip
.
推荐阅读
- javascript - 是否可以在文档 JavsScript 的顶部获取第一行 html
- ldap - 自由半径 + ldap 389 DS
- sql - 试图将一个表中的字段插入到另一个表中
- r - 绘制时间序列并计算超出阈值的次数
- ruby-on-rails - 运行 rails server 时无法加载此类文件 == rails/cli 错误
- jquery - 使用 Ajax 调用的结果填充复选框
- r - rvest read_html 用于特定表
- node.js - 本地的Vue.js,NGINX docker容器,如何用Node保存文件?
- cassandra - 使用 AvroConverter 启动时出现 Kafka Connect 错误
- sql - 如何按函数对一组结果进行数学函数(除法)