How do I tar a directory of files and folders without including the directory itself?

Viewed 606652

I typically do:

tar -czvf my_directory.tar.gz my_directory

What if I just want to include everything (including any hidden system files) in my_directory, but not the directory itself? I don't want:

my_directory
   --- my_file
   --- my_file
   --- my_file

I want:

my_file
my_file
my_file
21 Answers
cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd - 

should do the job in one line. It works well for hidden files as well. "*" doesn't expand hidden files by path name expansion at least in bash. Below is my experiment:

$ mkdir my_directory
$ touch my_directory/file1
$ touch my_directory/file2
$ touch my_directory/.hiddenfile1
$ touch my_directory/.hiddenfile2
$ cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd ..
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
$ tar ztf my_dir.tgz
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2

You can also create archive as usual and extract it with:

tar --strip-components 1 -xvf my_directory.tar.gz
cd my_directory
tar zcvf ../my_directory.tar.gz *

This Answer should work in most situations. Notice however how the filenames are stored in the tar file as, for example, ./file1 rather than just file1. I found that this caused problems when using this method to manipulate tarballs used as package files in BuildRoot.

One solution is to use some Bash globs to list all files except for .. like this:

tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *

This is a trick I learnt from this answer.

Now tar will return an error if there are no files matching ..?* or .[^.]* , but it will still work. If the error is a problem (you are checking for success in a script), this works:

shopt -s nullglob
tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
shopt -u nullglob

Though now we are messing with shell options, we might decide that it is neater to have * match hidden files:

shopt -s dotglob
tar -C my_dir -zcvf my_dir.tar.gz *
shopt -u dotglob

This might not work where your shell globs * in the current directory, so alternatively, use:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob

Command

to create a standard archive file.

find my_directory/ -maxdepth 1 -printf "%P\n" | tar -cvf my_archive.tar -C my_directory/ -T -

Packed files and dirs are in the root of the archive without path info and deeper files have relative path.
There are no weird looking './' in front of files and dirs. ('./file')
No special files '.' are included.

It seems that another tool, like find or ls (ls -A -1) is needed to accomplish these goals and tar using just its arguments is unable to pick files and create an archive with such requirements.

Using above command creates an archive tar file which can be further processed or delivered to someone without looking weird or needing an explanation or a tool to unpack.

Arguments description

-maxdepth 1
Descend at most 1 level - No recursing.
-printf
print format on the standard output
%P File's name with the name of the starting-point under which it was found removed.
\n Newline
printf does not add a newline at the end of the string. It must be added here

tar:
-C DIR, --directory=DIR
change to directory DIR

-T FILE, --files-from=FILE
get names to extract or create from FILE
-
that FILE from above is the standard input, from the pipe


Comments on other solutions.

The same result might be achieved using solution described by @aross.
The difference with the solution here is in that which tool is doing the recursing. If you leave the job to find, every filepath name, goes through the pipe. It also sends all directory names, which tar with --no-recursion ignores or adds as empty ones followed by all files in each directory. If there was unexpected output as errors in file read from find, tar would not know or care what's going on.
But with further checks, like processing error stream from find, it might be a good solution where many options and filters on files are required.
I prefer to leave the recursing on tar, it does seem simpler and as such more stable solution.
With my complicated directory structure, I feel more confident the archive is complete when tar will not report an error.

Another solution using find proposed by @serendrewpity seems to be fine, but it fails on filenames with spaces. Difference is that output from find supplied by $() sub-shell is space-divided. It might be possible to add quotes using printf, but it would further complicate the statement.

There is no reason to cd into the my_directory and then back, while using ../my_archive.tar for tar path, because TAR has -C DIR, --directory=DIR command which is there just for this purpose.

Using . (dot) will include dots

Using * will let shell supply the input file list. It might be possible using shell options to include dot files. But it's complicated. The command must be executed in shell which allows that. Enabling and disabling must be done before and after tar command. And it will fail if root dir of future archive contains too many files.

That last point also applies to all those solutions which are not using pipe.

Most of solutions are creating a dir inside which are the files and dirs. That is barely ever desired.

If it's a Unix/Linux system, and you care about hidden files (which will be missed by *), you need to do:

cd my_directory
tar zcvf ../my_directory.tar.gz * .??*

I don't know what hidden files look like under Windows.

cd DIRECTORY
tar -czf NAME.tar.gz  *

the asterisk will include everything even hidden ones

This is what works for me.

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -maxdepth 1 -printf '%P ')

You could also use

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -mindepth 1 -maxdepth 1 -printf '%P ')

In the first command, find returns a list of files and sub-directories of my_dir. However, the directory my_dir is itself included in that list as '.' The -printf parameter removes the full path including that '.' and also all However the in the format string '%P ' of printf leaves a remnant in the list of files and sub-directories of my_dir and can be seen by a leading space in the result of the find command.

That will not be a problem for TAR but if you want to fix this, add -mindepth 1 as in the second command.

tar -czvf mydir.tgz -C my_dir/ `ls -A mydir`

Run it one level above mydir. This won't include any [.] or stuff.

function tar.create() {
        local folder="${1}"
        
        local tar="$(basename "${folder}")".tar.gz
        
        cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
}

Example:

tar.create /path/to/folder

You are welcome.

tar archive:

# tar -czvf mydir.tar.gz mydir

untar archive:

# tar -xf mydir.tar.gz
tar -C my_dir -zcvf my_dir.tar.gz `ls my_dir`
Related