I'm trying to include files that match */*/.thisdirectory/*
in a tar archive. What's the correct syntax? Apparently this is not:
> find .
.
./bar
./bar/.a
./bar/.a/baz
./foo
./foo/.a
> tar --include "*[.]a*" -cvf a.tar .
>
I'm assuming you're using FreeBSD tar¹ as this is the only implementation I know with a --include
option.
When you pass --include
, only explicitly included files are traversed. When building an archive, if a directory isn't traversed then neither it nor any of its contents can be included. Since you're tarring .
but not including .
, the sole command line argument doesn't get included, and you get an empty archive.
To reach the .a
directories, you would need to include all the parent directories. The thing is, if you do that, then all of their contents will be included as well. You can use --exclude
to exclude them, but then you need an exclude pattern that doesn't exclude the dot directories that you want to include. At this point --include
is no longer of any use. The --include
option is rarely useful when creating an archive, it's rather more useful when copying part of an existing archive (because when doing that, there's no recursive traversal, just iteration through the list of members of the input archive).
tar --exclude='./*/[!.]*' -cvf a.tar .
Note that all files in the toplevel directory are included. I don't think there's a way to include only those directories that will have non-empty contents.
To get only the ./*/.*
directories and nothing else (in particular not their parent directories), you need to pass them as arguments to tar, rather than let tar recurse to find them.
tar -cvf a.tar ./*/.[!.]*/
Remove the trailing slash to include files that aren't directories (or symlinks to directories). Add ./*/..?*/
to include directories whose name begins with ..
if you have those.
¹ Possibly under a different operating system.
If your shell is bash, you can use the GLOBIGNORE
variable to hide the .
and ..
directories. This does automatically also set the dotglob
option, so *
now matches both hidden and non-hidden files:
GLOBIGNORE=".:.."
tar czf tarball-with-hidden-files.tgz directory/*
To expand on this a bit: by default, *
does not match hidden files (files starting with a .
), which includes the current (.
) and parent (..
) directories of any directory. If you want a glob (e. g. a command line wildcard) to match them, you need to set the dotglob
option, but this can cause really bad things to happen if ..
matches *
and you do something like rm -fr /home/olduser/*
(because olduser/..
matches, which is /home/
and now you've just fried everyones' home directories. Which is where GLOBIGNORE
comes into play- it blacklists things from matching wildcards. As a really simple use-case:
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
What does this mean for you? Well, as in the quick example at the top of this answer, once you set GLOBIGNORE
to ignore .
and ..
, you can use *
to match hidden directories like the ones you're trying to archive:
tar czf mytarball.tgz directory/* # this will include things like directory/.hiddenfile
To point this at your specific example of a directory called .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
> GLOBIGNORE=".:.." tar --include "*[.]a*" -cvf a.tar . >
The .* directories get included just fine without --include
but trying to restrict what gets tarred fails utterly...
*/*/.thisdirectory/*
so clarifications are needed on what files you want to include.
May 6, 2016 at 23:58
find . | grep "[.]thisdirectory" > files; tar -cf my.tar -T files
does what I want, this is all academic, but I get tired of SO being so willfully pedantic in response to all questions.
Rather jumping on an old horse here, but, since I can't find any evidence of anyone pointing out the apparently "intended" way that this should be done with tar:
The issue, as has been noted elsewhere, is that tar is quite literal with "--include", and it won't traverse into directories foo and bar to find your .a files, if the --include filters them off first.
The clue to the trick to using tar as you'd like to use it /is/ included in the man page, but it's fairly well obfuscated: --include is "supposed" to be used to filter existing archives; Also, there is a magic argument @- that causes tar to treat a read from STDIN as a read from an existing archive.
Put together, you can use one promiscuous tar statement to create an archive of everything below the current directory, and stuff it onto STDOUT. You then pipe this into a tar that's going to use the --include option, and that reads from STDIN.
In your case, this looks something like:
tar -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