Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I can probably write a shell script to find files only, then pass the list to tar, but I am wondering whether there already is a built-in feature in tar that allows doing just that, in a single command line?

For example, I found the --no-recursion switch, but when I do:

tar --no-recursion -cvf mydir.tar mydir

It only archives the names of the entries in the directory (including subdirectories!), but it doesn't archive any files.

I also tried:

 tar --no-recursion -cvf mydir.tar mydir/*

But while it archives files only, it also archives the names of the subdirectories.

Is there a way to tell tar files only, no directories?

share|improve this question
4  
Just to clarify: do you want to create an archive with "flat" structure (i.e. all files mixed up in one directory)? –  rozcietrzewiacz Nov 18 '11 at 14:47
1  
You could create a new directory and find mydir -type f |xargs cp -t tempdir and then tar tempdir. –  Kevin Nov 18 '11 at 15:05
    
@rozcietrzewiacz Yes, flat, but only from that directory, not from subdirectories. –  ateiob Nov 18 '11 at 15:17
1  
OK, I think I see what you're trying to do. How about find mydir -depth 1 -type f | xargs tar cf mydir.tar –  Kevin Nov 18 '11 at 15:30
2  
Ah, spaces. Use find's -exec instead: find mydir -maxdepth 1 -type f -exec tar cvf mydir.tar {} +. The + puts all the files on the same command line like xargs. –  Kevin Nov 18 '11 at 15:49
show 5 more comments

5 Answers

up vote 6 down vote accepted

As camh points out, the previous command had a small problem in that given too many file names, it would execute more than once, with later invocations silently wiping out the previous runs. Since we're not compressing too, we can append instead of overwrite:

find mydir -maxdepth 1 -type f -print0 | xargs -0 tar Avf mydir.tar
find mydir -maxdepth 1 -type f -exec tar Avf mydir.tar {} +

Iocnarz's answer of using tar's --null and -T options works as well. If you have cpio installed, camh's answer using it is also fine. And if you have zsh and don't mind using it for a command, Gilles's answer using a zsh glob (*(.)) seems the most straightforward.


The key was the -maxdepth option. Final answer, dealing with spaces appropriately:

find mydir -maxdepth 1 -type f -print0 | xargs -0 tar cvf mydir.tar

This should also work:

find mydir -maxdepth 1 -type f -exec tar cvf mydir.tar {} +
share|improve this answer
2  
Both of these can result in multiple invocations of tar. Both xargs and find with the + variant has a maximum number of arguments that can be passed to tar. This will result in the second invocation of tar overwriting the output of the first. –  camh Apr 6 '12 at 22:30
1  
Expanding on the xargs limit, xargs default to a maximum 128KiB command line. If the file list is larger, you get a second (or more) invocation of the command (tar), leading to silent data loss. You can use -x to force xargs to fail instead of losing data, and while better than a silent data loss bug, it's still not ideal. This sort bug is so dangerous because everything seems OK at first, but as the file list grows over time, you start triggering it and may not notice until you try to restore your backup. Then it's too late. –  camh Apr 6 '12 at 22:49
    
@camh You're right, thanks for pointing that out; I've updated to reflect that. –  Kevin Apr 7 '12 at 3:22
    
The "final answer" is not correct, if you use "tar c" without -T you might get partial results. –  eckes May 31 '13 at 17:27
add comment

As the introductory paragraph in man tar says (last sentence),

The use of a directory name always implies that the subdirectories below should be included in the archive.

Which I understand as a "no" answer to your question.

share|improve this answer
    
Good catch, except that tar now includes numerous exclusion meachisms (e.g. --no-recursion, --exclude-tag, etc.). I am looking into --exclude-tag which looks promising but seems to be the exact opposite of what I am looking for. –  ateiob Nov 18 '11 at 15:21
add comment

I'm not sure I understand your requirements. If you want to store the regular files in mydir but not its subdirectories, the easiest way is to use zsh, where matching regular files only is the simple matter of using the . glob qualifier:

tar cf mydir.tar mydir/*(.)
share|improve this answer
add comment

You may even use find ... -print0 and tar ... --null directly without using xargs at all.

find . -maxdepth 1 -type f -print0 | tar cvf mydir.tar --null -T -

In the given example, the --no-recursion option to tar is not necessary because only paths of files (and not directories) will be passed from find to tar.

Using the --no-recursion option to tar in the following example, however, prevents tar from double archiving directories. find will do the directory tree recursion instead of tar then.

# compare
find . -print0 | tar cf mydir.tar --null -T -
tar -tf mydir.tar | nl

find . -print0 | tar cf mydir.tar --null --no-recursion -T -
tar -tf mydir.tar | nl
share|improve this answer
add comment

When you want to use find with tar, the best way is to use cpio instead of tar. cpio can write tar archives and is designed to take the list of files to archive from stdin.

find mydir -type f -maxdepth 1 -print0 | cpio -o -H ustar -0 > mydir.tar

Using find and cpio is a more unix-y approach in that you let find do the file selection with all the power that it has, and let cpio do the archiving. It is worth learning this simple use of cpio, as you find it easy to solve problems you bang your ahead against when trying tar.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.