Linux – Move or copy the contents of one directory into another

Linux TuxThe way Linux expands it’s wildcard, or globbing characters, can be confusing at first and even tasks as simple as copying a few files can sometimes turn into a learning exercise. It is sometimes necessary to move or copy files in bulk from one place to another. There are several methods for doing this in Linux, depending on which tools you have installed (Obviously, tools like rsync or unison might be better in many cases), but for this article we’re just sticking to mastering the cp command.

It’s important to understand how Linux sees the contents of each directory, because there are two special entries within each directory which people often overlook – plus there is the issue of hidden dot files – files whose names start with th dot – e.g.  .htaccess

# ls -a1
./  << This is the current directory hard link
../  << This is the parent directory hard link
new/
old/

Every Linux directory contains a hard link to itself under the name of “dot” (.) It also contains a hard link to its parent, called “dot dot” (..). It is important to understand how you can and should use these links when carrying out basic file management in Linux, as they can save a lot of time.

In this exercise we will be moving the contents of /old into /new

Here is our imaginary file structure

/old
/old/.htaccess
/old/file1
/old/file2

Now, if the /new directory did not exist, then we could simply use

cp -dR --preserve=all /old /new

or, if both directories were in the current working directory, you could drop the leading slashes.

cp -dR --preserve=all old new

Some Linux builds support the -a option with cp and mv, which replaces the -dR –preserve=all switches, so for clarity, we’ll use that from here onwards. Now, the fun can start when the destination directory already exists. Consider if you were moving or copying a directory of files into an existing user’s public_html directory…

So, sounds easy enough… lets try this all again, only this time with a pre-existing /new directory… running the copy commands as above will end up placing the source directory inside the destination directory, so we end up with:

/new/old
/new/old/.htaccess
/new/old/file1
/new/old/file2

Not really what we were looking for. The next common mistake made is to try copying the files by using wildcards, like this:

cp -a /old/* /new

Great! until you notice that this has omitted to copy any of the hidden dot files. This is because when the command is expanded, the asterisk globbing pattern doesn’t match the initial dot character. You could of course then try (but PLEASE don’t):

cp -a /old/.* /new

Arrggh, even worse… you’ve now copied not just the contents of the /new directory, but also it’s parent (which could potentially fill up your mount point!)… To exclude copying the parent directory (i.e. matching the .. directory entry) we can use the ? wildcard, which won’t match a dot but will match anything else.

cp -a /old/.??* /new

OK, so now we have worked out that by running two command, we can copy all regular files, and then all hidden files. However, in best Linux tradition, we’re lazy geeks, and want to find a way of doing this in a one liner. The way to achieve what we are looking for (on most, default linux distros) is:

cp -a /old/. /new

What we are actually saying here, is copy in archive mode (the -a switch, which is recursive) the current directory contents of /old, but not /old itself (the single dot – remember, the one that comes up at the top of your ls command output – this represent the current directory, and has nothing to do with hidden files) into the /new directory. It’s a subtle, but beautiful difference!

Moving the contents of a directory into its parent

OK, so that’s copying. You could just copy, and then remove the old directory, but the problem is sometimes it’s just easier to move files – preserving their ownership and permissions (Copying files as root as not a great idea in a shared environment – you might momentarily provide root power to a bogus script!)

Moving without an intermediate copy is not possible using the mv command alone, as it has no recursive archive switch ‘-a’ like copy. Instead we use the find command and the mv command together.

So if you have a structure:

/home/user/public_html/wordpress

and wanted to move the entire contents of the wordpress directory up a level into the public_html directory:

cd /home/user/public_html

find wordpress -maxdepth 1 -mindepth 1 -exec mv {} . \;

rmdir wordpress

Job done :-)

Tags: , ,

No comments yet.

Leave a Reply