我刚刚尝试在创建 tar 存档时排除几个目录。目录结构相当简单(Centos 6,tar v1.23):
/test/t1
/t2
/t3
...
每个子目录(t1、t2、t3、...)包含一些 txt 文件。没什么不寻常的。
好的,我们来试试这个:
tar czvf test.tar.gz test/ --exclude={"t2"}
失败,t2
子目录包含在档案中。
tar czvf test.tar.gz test/ --exclude={"t2",""}
成功,t2
被排除——正如预期的那样。
我尝试在笔记本电脑(Ubuntu18.04,tar v1.29)上使用相同的目录结构重现相同的情况。这里,两个命令都失败了 - 目录t2
包含在创建的存档中!
- 为什么提供的单个目录条目
{}
不起作用? - 为什么在不同的环境下结果会不同?
这是怎么回事?这与 tar 版本有关吗?依赖于 Linux 发行版?查看 tar 手册(当前版本,v1.32)和 tar 更新日志都没有给我任何答案。
答案1
我们从结尾开始吧。
为什么在不同的环境下结果会不同?
在某些版本中,tar
--exclude=…
仅对位于test/
其后的路径(例如)进行计数。这意味着它tar … test/ --exclude=t2
就像根本没有一样工作--exclude
。我的 Debian 9 中的 GNU tar
1.29 肯定是这样的。
您声称在 1.23 中--exclude
,路径有效(至少在某些情况下)。我没有理由不相信您。事实上,我tar
在 Debian 7 中达到了 GNU 1.26,它的行为就像您描述的 1.23 一样。
结论:放置--exclude=…
在类似路径之前test/
。
为什么提供的单个目录条目
{}
不起作用?
这篇文章不是关于 的tar
,而是关于 shell 和 shell 执行(或不执行)的括号扩展。并非所有 shell 都这样做。Bash 确实,Zsh 也是如此。普通 POSIX shell 没有这样的功能。
在支持括号扩展的 shell 中调用:
printf '<%s> ' --exclude={"t2"}; echo
printf '<%s> ' --exclude={"t2",""}; echo
printf '<%s> ' --exclude={foo,bar,"baz qux"}; echo
在每个输出中,无论里面是什么,<>
都会得到一个单独的参数printf
。您将分别看到:
<--exclude={t2}>
<--exclude=t2> <--exclude=>
<--exclude=foo> <--exclude=bar> <--exclude=baz qux>
我以前<>
能够肯定地说可能存在多个参数,而不是带有空格的单个参数。
您可能认为--exclude={foo,bar,baz}
make {foo,bar,baz}
get totar
并且该工具会将其解释为某种列表。不。shell 扩展了此语法并生成多种的启动之前的单词tar
。该工具获取这些单词作为命令行参数,将它们解释为选项,并且不知道其中涉及一些括号;就像printf
获取其参数一样。
现在出现了一个怪癖:要进行括号扩展,括号内必须至少有一个逗号 ( ,
) 或点点 ( ,它有不同的用途)。来自 Bash 的链接手册:..
正确格式的括号扩展必须包含未加引号的左括号和右括号,以及至少一个未加引号的逗号或有效的序列表达式。任何格式错误的括号扩展均保持不变。
我们可能会调用--exclude={"t2"}
一个格式错误的括号扩展。字符串保持不变,tar
几乎与输入时一样(几乎,因为引号被删除)。该工具将排除名为 的文件{t2}
。如您所见,没有理由排除t2
;t2
不是{t2}
。
另一方面,--exclude={"t2",""}
是正确形成的括号扩展,它扩展为--exclude=t2
--exclude=
。后者不排除任何内容,但tar
不会抱怨。前者t2
按照您的意愿排除。
--exclude={foo,bar,baz,whatever}
借助 shell 的功能,可以节省您一次又一次的输入--exclude=
。但最终tar
会得到
--exclude=foo --exclude=bar --exclude=baz --exclude=whatever
就像您输入了四条--exclude=
语句一样。
如果只是要foo
排除,那么您就不能将“列表”缩短为--exclude={foo}
,而应该只是--exclude=foo
。您发现您可以使用--exclude={foo,}
,但这非常麻烦且毫无用处。请记住,这些括号不是必需的tar
,它们永远不会到达那里。
请注意,如果您想排除任何文件(任何类型(包括目录类型的文件)字面上地那么{foo,bar,baz,whatever}
您需要引用以防止大括号扩展开始:
--exclude='{foo,bar,baz,whatever}'