使用 tar 包含 .* 目录的正确语法

使用 tar 包含 .* 目录的正确语法

我正在尝试将匹配的文件包含*/*/.thisdirectory/*在 tar 存档中。正确的语法是什么?显然这不是:

> find .
.
./bar
./bar/.a
./bar/.a/baz
./foo
./foo/.a
> tar --include "*[.]a*" -cvf a.tar .
>

答案1

我假设您使用的是 FreeBSD tar1,因为这是我所知道的唯一--include有选项的实现。

当您传递 时--include,只会遍历显式包含的文件。构建存档时,如果未遍历目录,则无法包含该目录或其任何内容。由于您正在压缩.但不包含.,因此不会包含唯一的命令行参数,并且您会得到一个空存档。

要访问.a目录,您需要包含所有父目录。问题是,如果你这样做,那么它们的所有内容也将被包含在内。您可以使用--exclude排除它们,但是您需要一个不排除您想要包含的点目录的排除模式。此时--include已经没有任何用处了。该--include选项在创建存档时很少有用,在复制现有存档的一部分时更有用(因为这样做时,没有递归遍历,只是迭代输入存档的成员列表)。

tar --exclude='./*/[!.]*' -cvf a.tar .

请注意,包含顶级目录中的所有文件。我认为没有办法只包含那些具有非空内容的目录。

要仅获取./*/.*目录而不获取其他内容(特别是不获取其父目录),您需要将它们作为参数传递给 tar,而不是让 tar 递归查找它们。

tar -cvf a.tar ./*/.[!.]*/

删除尾部斜杠以包含不是目录的文件(或目录的符号链接)。添加./*/..?*/以包含名称开头的目录(..如果有)。

¹可能在不同的操作系统下。

答案2

如果您的 shell 是 bash,则可以使用该GLOBIGNORE变量来隐藏...目录。这也会自动设置dotglob选项,因此*现在会匹配隐藏和非隐藏文件:

GLOBIGNORE=".:.."
tar czf tarball-with-hidden-files.tgz directory/*

稍微展开一下:默认情况下,*不匹配隐藏文件(以 开头的文件.),其中包括任何目录的当前 ( .) 目录和父 ( ) 目录。..如果你想要一个 glob(例如命令行通配符)来匹配它们,你需要设置该dotglob选项,但这可能会导致真的..如果匹配*并且你做了类似的事情,就会发生不好的事情rm -fr /home/olduser/*(因为olduser/..匹配,/home/现在你刚刚炒了每个人的主目录。这就是GLOBIGNORE发挥作用的地方 - 它会将匹配通配符的内容列入黑名单。作为一个非常简单的用例:

ghoti@home:~/scratch$ ls -ld ?dotfile
ls: cannot access '?dotfile': No such file or directory
ghoti@home:~/scratch$ touch _dotfile .dotfile
ghoti@home:~/scratch$ ls -ld ?dotfile
-rw-rw-r-- 1 ghoti ghoti 0 May  6 16:58 _dotfile
ghoti@home:~/scratch$ GLOBIGNORE=".:.."
ghoti@home:~/scratch$ ls -ld ?dotfile
-rw-rw-r-- 1 ghoti ghoti 0 May  6 16:58 _dotfile
-rw-rw-r-- 1 ghoti ghoti 0 May  6 16:58 .dotfile

这对你来说意味着什么?好吧,正如本答案顶部的快速示例所示,一旦您设置GLOBIGNORE为忽略...,您就可以使用*它来匹配隐藏目录,例如您尝试存档的目录:

tar czf mytarball.tgz directory/*  # this will include things like directory/.hiddenfile

将其指向名为 的目录的特定示例.a

ghoti@home:~/scratch$ mkdir -p example example/foo/.a example/bar/.a example/foo/_a example/bar/_a
ghoti@home:~/scratch$ touch example/{foo,bar}/{.,_}a/file
ghoti@home:~/scratch$ find example/
example/
example/foo
example/foo/_a
example/foo/_a/file
example/foo/.a
example/foo/.a/file
example/bar
example/bar/_a
example/bar/_a/file
example/bar/.a
example/bar/.a/file
ghoti@home:~/scratch$ GLOBIGNORE=".:.."
ghoti@home:~/scratch$ tar cvzf withdotfiles.tgz example/*/?a
example/bar/_a/
example/bar/_a/file
example/bar/.a/
example/bar/.a/file
example/foo/_a/
example/foo/_a/file
example/foo/.a/
example/foo/.a/file
ghoti@home:~/scratch$ unset GLOBIGNORE
ghoti@home:~/scratch$ tar cvzf withoutdotfiles.tgz example/*/?a
example/bar/_a/
example/bar/_a/file
example/foo/_a/
example/foo/_a/file

答案3

而是在这里跳上一匹老马,但是,因为我找不到任何人指出明显“预期”的方式的证据,这应该用 tar 来完成:

正如其他地方所指出的,问题在于 tar 与“--include”相当字面意思,如果 --include 首先将它们过滤掉,它不会遍历目录 foo 和 bar 来查找 .a 文件。

使用 tar 的技巧的线索 /is/ 包含在手册页中,但它相当容易混淆: --include “应该”用于过滤现有档案;此外,还有一个神奇的参数 @- 使 tar 将从 STDIN 读取的数据视为从现有存档读取的数据。

总之,您可以使用一个混杂的 tar 语句来创建当前目录下所有内容的存档,并将其填充到 STDOUT 中。然后将其通过管道传输到将使用 --include 选项的 tar 中,从 STDIN 读取。

在你的情况下,这看起来像:

焦油 -cf -/ | tar -cf myfiles.tar --include =“。A” @-

Soyokaze:test$ ls -lR
total 32
drwxr-xr-x   6 tyler  staff   204 Nov 29 14:24 bar/
drwxr-xr-x   4 tyler  staff   136 Nov 29 14:23 foo/

./bar:
total 0
-rw-r--r--  1 tyler  staff    0 Nov 29 14:23 baa.a
-rw-r--r--  1 tyler  staff    0 Nov 29 14:23 baaa.a
-rw-r--r--  1 tyler  staff    0 Nov 29 14:23 baz
drwxr-xr-x  2 tyler  staff   68 Nov 29 14:24 boos/

./bar/boos:
total 0

./foo:
total 0
-rw-r--r--  1 tyler  staff    0 Nov 29 14:23 baa.a
-rw-r--r--  1 tyler  staff    0 Nov 29 14:23 baz.a

Soyokaze:test$ tar -cf - */ | tar -cf myfiles.tar --include="*.a" @-

Soyokaze:test$ tar -tf myfiles.tar
bar/baa.a
bar/baaa.a
foo/baa.a
foo/baz.a

相关内容