1

This is driving me nuts. All google results are for the reverse scenario.

So I have a directory structure like:

MyFiles
|    file1
|    file2
|    file3
|    MoreFiles
|    |    file4
|    |    file5

And what I want is an archive (preferably zip as I'm passing along process to non-tar users, but bonus points for answers showing how to in zip and tar) that looks like:

archive.zip
|    MoreFiles
|    |    file4
|    |    file5

So that the top-level files are excluded but the sub-directories and their files are include.

So far, the best I could come up with is:

zip -r archive.zip MyFiles -x "."

but this excludes all files at every level, so I just end up with the directory structure. I only want to exclude the directories sitting directly in the target directory.

Oh! And actually, my real-world use-case is to exclude the contents of one specific sub-directory, so working from the next-deepest level isn't a workaround. The real scenario is:

MyFiles
|    file1
|    file2
|    file3
|    Level1Files1
|    |    file4
|    |    file5
|    |    Level2Files1
|    |    |    file6
|    |    |    file7
|    |    Level2Files2
|    |    |    file8
|    |    |    file9
|    Level1Files2
|    Level1Files3

with the end goal of including everything at the top level, but exclude files inside specific subdirectory, but not exclude the subdirectories inside that first subdirectory or their contents, so that if I wanted to exclude top files of subdirectory Level1Files1 in above , the result would be:

archive.zip
|    file1
|    file2
|    file3
|    Level1Files1
|    |    Level2Files1
|    |    |    file6
|    |    |    file7
|    |    Level2Files2
|    |    |    file8
|    |    |    file9
|    Level1Files2
|    Level1Files3

1 Answer 1

1

With creative uses of wildcards.

$ tree MyFiles
MyFiles
├── dira
│   ├── file1
│   ├── file2
│   └── file3
├── dirb
│   ├── file1
│   ├── file2
│   └── file3
├── dirc
│   ├── file1
│   ├── file2
│   └── file3
├── file1
├── file2
└── file3

What we need to do is just not tell zip about the root files. Bash wildcards allow us to specify this with zip -r archive.zip MyFiles/*/ Note that the wildcard is formatted so that it can only match a directory. Using that, we get:

$ zip -r archive.zip MyFiles/*/
  adding: MyFiles/dira/ (stored 0%)
  adding: MyFiles/dira/file1 (stored 0%)
  adding: MyFiles/dira/file2 (stored 0%)
  adding: MyFiles/dira/file3 (stored 0%)
  adding: MyFiles/dirb/ (stored 0%)
  adding: MyFiles/dirb/file1 (stored 0%)
  adding: MyFiles/dirb/file2 (stored 0%)
  adding: MyFiles/dirb/file3 (stored 0%)
  adding: MyFiles/dirc/ (stored 0%)
  adding: MyFiles/dirc/file1 (stored 0%)
  adding: MyFiles/dirc/file2 (stored 0%)
  adding: MyFiles/dirc/file3 (stored 0%)

$ less archive.zip
Archive:  archive.zip
Zip file size: 1960 bytes, number of entries: 12
drwxrwxr-x  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dira/
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dira/file1
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dira/file2
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dira/file3
drwxrwxr-x  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirb/
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirb/file1
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirb/file2
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirb/file3
drwxrwxr-x  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirc/
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirc/file1
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirc/file2
-rw-rw-r--  3.0 unx        0 bx stor 15-Apr-08 00:30 MyFiles/dirc/file3
12 files, 0 bytes uncompressed, 0 bytes compressed:  0.0%

Subdirectories and their contents only.

For the bonus question:

tar -xcvf archive.tar.gz MyFiles/*/

More specific exclusions would require very creative use of wildcards, regex, find, and pipes. A couple ideas come to mind, but nothing pretty or reliable. At that point you're getting into scripting, and you might as well just iterate through with loops.

Edit: there's a not-totally-insane way to do it with subdirs. I think.

zip -r archive.zip MyFiles/*/ -x \
         $(find ./MyFiles/subdir/toexclude/topfiles -maxdepth 1 -not -type d)

Take note of the escaped newline - this should all be one line. Modify as necessary. I've tested it, and it excluded the files in the MyFiles/subdir/toexclude/topfiles but included its 3 subdirectories and so on.

This should do it for your example:

$ cd MyFiles
$ zip -r archive.zip * -x $(find -maxdepth 1 ./Level1Files1 -not -type d)
1
  • Holy moly nice job, both in covering that there is a sane simple way for top level files and an only slightly scary approach for specific subdirectory files. Very nice! I actually resisted posting a question until I finally decided that if I couldn't easily find a solution, then probably most people can't, so hopefully this will be the go to resource for this scenario.
    – Anthony
    Apr 8, 2015 at 5:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .