filter in "find" ignored when output fed to tar

I have the following directory structure:

test/
test/1/
test/foo2bar/
test/3/

I want to compress directory “test” excluding everything which is in subdirectories (depth not predefined), which include strings “1” or “2” in them. In bash shell, i want to use find and feed its output to tar. I first test find:

find test/ -not -path "*1*" -not -path "*2*"

Output:

test/
test/3

Great. So i combine it with tar:

find test/ -not -path "*1*" -not -path "*2*" | tar -czvf test.tar.gz --files-from -

Output:

test/
test/3/
test/1/
test/foo2bar/
test/3/

Indeed, both “test/1” and “test/foo2bar” are present in the archive. Why were these arguments passed to tar, if they were not supposed to be present in find output?

Asked By: InternazionalIV

||

To expand on what @cuonglm said, tar by default operates recursively. If you pass it a directory name, it will archive the contents of that directory.

You could modify your find command to return only the names of files, not directories…

find test/ -type f -not -path "*1*" -not -path "*2*" |
tar -czvf test.tar.gz --files-from -

You could instead use the --no-recursion flag to tar:

find test/ -not -path "*1*" -not -path "*2*" | 
tar -czvf test.tar.gz --no-recursion --files-from - 

Which results in:

test/
test/3/

The --no-recursion flag is specific to GNU tar. If you’re using something else, consult the appropriate man page to see if there is a similar feature available.

Note that your find command will exclude files that contain 1 or 2 in the path as well as directories.

Answered By: larsks

With GNU tar, you can also use the --exclude option to exclude files based on names.

$ tar --exclude "*1*" --exclude "*2*" -cvf foo.tar  test/
test/
test/3/

There’s also -X or --exclude-from which takes a file from which to read the exclusion patterns.


Though as find -not -path "*1*", this will also exclude files whose names contain a 1 or 2. To only skip directories whose names match the pattern, use find -prune and tar --no-recursion:

$ touch test/3/blah.1
$ find test/ -type d ( -name "*1*" -o -name "*2*" ) -prune -o -print |
   tar cvf test.tar --files-from - --no-recursion
test/
test/3/
test/3/blah.1

(At least GNU tar and FreeBSD tar have --no-recursion)

Answered By: ilkkachu
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.