I've encapsulated the order that find should place when looking for the
files, but it's not necessary in this example. It's just an aggregation of
the command we saw earlier.
[n6tadam@station tar]$ find . \( -path "./foo" -prune -o -path "./foo2" \) -pr
une -o -print
.
./a
./b
./c
... which leaves us with the desired result.
Now the fun stuff. How do you get tar to use the results given to produce
a tar file? For ease of use, we'll modify our find command to show the
filenames with the full path name, rather than "./" (which isn't at all
helpful to us):
[n6tadam@station tar]$ find $(pwd) \( -path "$(pwd)/foo" -prune -o -path "$(pwd
)/foo2" \) -prune -o -print
/tmp/tar
/tmp/tar/a
/tmp/tar/b
/tmp/tar/c
So we can see what's what. You might think that it's just a case then of
doing:
find $(pwd) \( -path "$(pwd)/foo" -prune -o -path "$(pwd)/foo2" \) -prune -o -p
rint -exec tar -czvf ./foofile.tgz {} \;
... but in fact that won't work, since what that does is runs the command
like this:
tar -czvf ./foofile.tgz /tmp/tar
tar -czvf ./foofile.tgz /tmp/tar/a
tar -czvf ./foofile.tgz /tmp/tar/b
tar -czvf ./foofile.tgz /tmp/tar/c
... but, there are two things wrong with this. One, is that it's
specifying "/tmp/tar" as a valid entry to our tar file. That's not what we
want -- we *don't* want that recursive nature to tar -- so already that's
put pay to the whole of the find command (more about that in a minute).
The second problem is that each time that tar command runs, it's replacing
the tar file with the new file, rather than appending it. Ouch! So if you
were to look at that tar file now, all you would see is "/tmp/tar/c" since
that was the last file created in the tar file.
Tar supports the "-A" option -- to append to a tar file. But that
presupposes that the tar file is already in existence -- and the
assumption here is that it isn't. So we can't use it.
Also, using -exec on find is a terrible idea in this case, since it runs a
copy of the same command (tar in this case) for every file encountered,
and since the tar file is never created...
So, we'll use xargs. That builds up command-line input on a chain so that
when it is run, we'll see something like this:
tar -czvf ./foofile.tar /tmp/tar /tmp/tar/a /tmp/tar/b /tmp/tar/c
Which is exactly what we want. But we first have to ensure that we further
disclude that "/tmp/tar" entry. And there's an option to tar to do that:
"--no-recursion".
The other consideration to take into account are filenames. Even if you're
sure that the filenames are valid, etc., it is still good practise to
assume the worst. Modifying our initial find command, we can tell it to
split filenames based on '\0' (rather than what $IFS defines it as). The
"print0" option to find defines this:
find $(pwd) \( -path "$(pwd)/foo" -prune -o -path "$(pwd)/foo2" \) -prune -o -p
rint0
... which'll give us:
/tmp/tar/tmp/tar/a/tmp/tar/b/tmp/tar/c
Which by itself is useless. But in this situation, we can tell xargs to
reinterpret that via "xargs -0", so that's not a problem. It's just a
means of protecting the filenames so that they're not mangled.
So if we piece my ramblings together the actual command you'll want to use
is:
find $(pwd) -path "$(pwd)/foo" -prune -o -path "$(pwd)/foo2" -prune -o -print0
| xargs -0t tar --no-recursion -PSczf $(pwd)/foofile.tgz
Note the "-t" option to xargs(1). This just prints out the command (as you
might have typed it on the command-line) before it is executed.
As a check, we can now ensure that the above command worked:
[n6tadam@station tar]$ tar -tzf ./foofile.tgz
/tmp/tar/
/tmp/tar/a
/tmp/tar/b
=9= |