36

How do you copy all the contents of one directory into another?

For example:

$ cd /home/newuser
$ cp -a /backup/olduser/* .

The problem with the above is that the globing pattern '*' matches the hidden directories '.' and '..' and you end up with a directory 'olduser' inside 'newuser', as well as the contents.

You could also do something like this:

$ rmdir /home/newuser
$ cp -a /backup/olduser /home/newuser

But what if newuser already contains some default files and directories?

What is the simplest, most correct, easiest to remember and not mess-up way to move the contents of one directory to another using just the basic 'cp' command and the shell?

1

7 Answers 7

34

Try:

cp -ra /backup/olduser/. /home/newuser
5
  • This is great, but I can't figure out exactly how it works. I think it's probably because I don't understand how . and .. work. Care to explain? Aug 24, 2011 at 2:33
  • This worked for me but cp -r ./a b didn't. Any idea why?
    – Brig
    Feb 21, 2012 at 19:07
  • 5
    works for me too, but the r option is not needed. The a option implies r.
    – MountainX
    Apr 14, 2012 at 17:38
  • 1
    the -r parameter seems to be a bit redundant at this point! Sep 12, 2013 at 13:56
  • The -a parameter does nothing to help in this situation. The command cp -r ./a/. b will copy the contents, and only the contents, of directory a into directory b. The -a flag preserves links, timestamps etc., which is not relevant to the question. Oct 23, 2019 at 8:33
26

Two directories a and b.

Both have files in.

You are in a directory that contains a and b.

cp -r ./a b

-r = recursively.

8
  • Don't you want -a to get all files?
    – b w
    Aug 20, 2009 at 17:22
  • 8
    The -a argument is nothing do to with all files, it's short for archive, it will preserve file ownership, permissions, access times etc. The -a argument (while highly useful) isn't a standard option, and several cp implementations don't provide it. Aug 21, 2009 at 3:26
  • 1
    @mch: automatically included, because you've told cp to copy a directory a, and a/.somedotfile is contained by a. if b exists, this command will create an exact copy of a at b/a. Nov 8, 2009 at 0:33
  • 2
    Easy way: cp -R /some/dir/* /another/dir
    – nopcorn
    Jul 4, 2011 at 20:51
  • 1
    This copies a/foo to b/a/foo instead of to b/foo
    – Sparr
    Jul 30, 2012 at 15:48
19

Remember that, by default, cp copies the first directory into the second directory if the second exists.

For example cp -r a b will copy the directory a into b. If b does not exist, it will be created with the contents of a.

If you want to copy the content of a into b (for example when copying a whole filesystem into a mount point) use:

cp -r a/. b

as in the previous answer.

Please also note that -a, used in some of the other answers, is the same as -dr --preserve=all and will preserve timestamps, context and extended attributes.

5
  • How does the /. work? Aug 24, 2011 at 2:54
  • a/. represents the content of directory 'a' as opposed to the directory itself, in most situation it is synonymous but when using cp -r, it avoids cp default behaviour (copying a INTO b). Oct 20, 2011 at 11:19
  • awesome tip, this is exactly what I was looking for but failed to find elsewhere - all that 'foo/*' or '-r foo/' that fails in corner cases.
    – Tener
    Jun 1, 2013 at 16:46
  • @TimoHuovinen '.' is not a wildcard at all. It is literally the "parent" directory, and '..' is the grandparent. This means '/foo/./././././.' is the same directory as '/foo', and '/foo/bar/..' is the same directory as '/foo'. Think of them as hardlinks that are builtin to the filesystem.
    – quuxman
    Jun 20, 2014 at 10:57
  • @quuxman thank you, I wasn't aware of it back then. I'm gonna remove my old comment to avoid confusing people. Jun 20, 2014 at 19:35
6

Unless you have seriously reconfigured your shell, the globbing pattern '*' does not match '.' or '..', as you can verify using just echo *. What it does instead do is omit files whose name begins with a '.', so your approach will miss all hidden files. You can tweak some of this behavior with shell options, for example the dotglob option in bash, but it then won't be the portable and robust option that you are looking for.

If you need to do this more than once or twice, I recommend that you look into rsync or unison (depending on specific needs) with carefully crafted source and target specifications.

Another alternative is to put the source directory in a tarball and untar it over the existing target directory.

2
  • 1
    i use this: ( cd /src/dir ; tar cf - . ) | ( cd /dest/dir ; tar xf - ) ... the tar cf - . tar's the source directory, dotfiles included, and spits it to STDOUT, which gets piped to the STDIN of the tar xf -. Nov 8, 2009 at 0:37
  • I think the whole point of the OP is how to reference files that start with '.' but not the special '.' and '..' directories. There is a far more elegant solution above instead of using rsync or some other complex program.
    – quuxman
    Jun 20, 2014 at 10:59
2

This will copy both the normal and hidden files, while excluding the parent directory (..):

cd /directory/to/copy
cp -r * .[^.]* /destination/directory

If you do not exclude the parent directory, you end up with all of the contents of .. in your destination directory as well.

1
0

To copy files that begins with a dot just do cp .* target/

So easiest is just to do the cp command two times.

As Peter Eisentraut sais normal globbing rules do not include .. and . (hm, how to end this sentence? ;)

Just use -r to make it recursive and -i to make cp ask whether you really want to overwrite a file.

cp -ri /backup/olduser/* /newuser/
cp -ri /backup/olduser/.* /newuser/
2
  • Usually * does not match . or .., but it does if you use .*. Try echo .*. You can exclude . and .. with the following pattern: .[^.]*.
    – mch
    Nov 7, 2009 at 21:52
  • @mch: But .[^.]* still excludes names that begin with .. (e.g., ... or ..super-hidden). Jul 21, 2014 at 16:13
0

This works for copying all the files and directories except for hidden directories recursively from the current directory to wherever:

cp -rf * ^.* 

You must log in to answer this question.