![使用 tar 包含 .* 目录的正确语法](https://linux22.com/image/7451/%E4%BD%BF%E7%94%A8%20tar%20%E5%8C%85%E5%90%AB%20.*%20%E7%9B%AE%E5%BD%95%E7%9A%84%E6%AD%A3%E7%A1%AE%E8%AF%AD%E6%B3%95.png)
我正在尝试将匹配的文件包含*/*/.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