我只是在创建 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 更改日志给了我任何答案。
让我们从头开始。
在某些版本中,
tar
--exclude=…
仅计算其后的路径(例如test/
)。这意味着tar … test/ --exclude=t2
就像根本没有一样--exclude
工作。我的 Debian 9 中的GNUtar
1.29 的行为肯定是这样的。--exclude
在路径有效后(至少在某些情况下),您在 1.23 中声明。我没有理由不相信你。事实上,我tar
在 Debian 7 中达到了 GNU 1.26,它的行为就像你为 1.23 描述的那样。结论:放置
--exclude=…
在路径之前,例如test/
.这不是关于
tar
,而是关于 shell 执行(或不执行)的 shell 和大括号扩展。并非所有贝壳都这样做。Bash 可以,Zsh 也可以。普通的 POSIX shell 没有这样的功能。在支持大括号扩展调用的 shell 中:
在每个输出中,里面的任何内容
<>
都是一个单独的参数printf
。您将分别看到:我用了
<>
能够确定可能有多个参数,而不是一个带空格的参数。您可能已经想到
--exclude={foo,bar,baz}
make{foo,bar,baz}
gettar
并且该工具将其解释为某种列表。不,shell 扩展了这个语法并在之前生成了多个单词tar
。该工具将这些词作为命令行参数,将它们解释为选项,并且不知道其中涉及一些大括号;就像printf
得到它的论点一样。现在有个怪癖:要发生大括号扩展,大括号内必须至少有一个逗号 (
,
) 或点-点 (..
,它有不同的用途)。从 Bash 的链接手册中:我们可以称之为
--exclude={"t2"}
格式不正确的大括号展开。字符串保持不变,tar
几乎与键入时一样获取它(几乎,因为引号被删除)。该工具将排除一个字面上名为{t2}
. 如您所见,没有理由排除它t2
;t2
不是{t2}
。另一方面
--exclude={"t2",""}
是一个格式正确的大括号展开,它展开为--exclude=t2
--exclude=
. 后者不排除任何东西但tar
不抱怨。前者t2
根据需要排除。--exclude={foo,bar,baz,whatever}
--exclude=
由于外壳的功能,您可以一次又一次地节省打字。但最终tar
会得到就好像您输入了四个
--exclude=
语句一样。如果只是
foo
要排除,那么您不能将“列表”缩短为--exclude={foo}
,它应该是--exclude=foo
。你发现你可以使用--exclude={foo,}
,但这很麻烦而且没有任何用处。请记住,这些大括号不是 所必需的tar
,它们永远不会得到它。请注意,如果您想排除任何文件(任何类型的,包括类型目录的文件),那么您需要引用以
{foo,bar,baz,whatever}
防止大括号展开: