52

My question is why is it required to use the -r (recursive) flag when making a copy of a directory? I.e., why do this:

cp -r dir1 copyDir1

When would I not want this behavior when copying a directory?

Isn’t a recursive copy of a directory really the “default” behavior; the behavior we want nearly all the time?

It feels like this is a superfluous flag.

4
  • Wouldn't you need to copy the files and folder in it too? Nov 13, 2014 at 18:55
  • If you think this would be a improvement you can repost this request on developers channel. Otherwise it was probably programmed long time ago.
    – blogger
    Nov 13, 2014 at 18:55
  • 2
    This question has been asked and answered on unix.SE.
    – dotancohen
    Nov 15, 2014 at 22:13
  • Same goes for rm
    – user246682
    Nov 16, 2014 at 17:55

8 Answers 8

62

The way filesystems work, a directory is not actually a folder containing files but rather a directory is a file that contains inode pointers to “child” files connected to it. Meaning, from a file system perspective, a file is a file, but a directory is just a file containing list of connected files.

So from the command line perspective, doing this:

cp dir1 copyDir1

Would basically mean copy the file named, dir1 to a new file named copyDir1. And as far as the file system is concerned, dir1 is just a file anyway; the fact it’s a “directory” will only be apparent when the filesystem actually checks dir1 to see what that pile of bits actually is.

The -r flag tells the file system to recursively roll down the file/directory tree and copy any & all contents that might be a “child” of that file to a new place.

Now as to why that might seem superfluous or redundant, this really comes down to historic methods of dealing with file systems. As well as creating a system that is safe from all types of user related errors; accidental as well as intentional.

Meaning, let’s say you have a ~/bin file in your home directory you want to copy but accidentally left out the ~—because you are a human and make mistakes—so its just /bin like this:

cp /bin/ ~/copy_of_bin

With the “safety net” of /bin being a directory combined with the need for the -r flag you will avoid accidentally copying the whole binary root of the system you are on into your home directory. If that safety net did not exist, a minor—or possibly major—disaster would happen.

The logic here being that in the days pre-GUI (graphical user interfaces) logical/behavioral conventions need to be set to avoid having user created mishaps that can potentially kill a system. And using the -r flag is now one of them.

If that seems superfluous, then need look no further than modern GUI system one can place above Linux file systems. A GUI addresses basic user issues like this by allowing one to drag and drop files and directories with ease.

But in the case of the realm of text-based interfaces, lots of the “user experience” within that world is basically just logical and hueristic-based road bumps that help keep the user in check so potential disaster can be averted.

Similarly this is why Linux/Unix filesystems don’t have 777 permissions and sudo rights set by default and how real system administrators wince when a user sets 777 permissions or grants everyone sudo rights. These are the basic things one does to ensure the system is stable and as “user proof” as possible; anyone rushing to short-circuit those conventions will most likely cause damage to their system without even knowing it.


Additional Info: Another answer here on the Unix Stack Exchange site gives a good explanation of why a non-recursive copy of a directory is problematic; emphasis is mine.

Well, without the -R flag, it's only possible to copy files, because it's rather unusual that someone wants to non-recursively copy a directory: A non-recursive copy would just result in a second name for the directory, pointing to directly the same directory structure. Because that's rarely what people want, and there is actually a separate program that does this (ln), a non-recursive copy of directories is not allowed.

So if a directory is just really a file with inode items inside of it, making a straight copy of that file would just be the equivalent of how a hard link would work. Which is not what anyone wants.

6
  • 21
    I personally think the "protection" aspect of it doesn't pass the smell test. Some human could just as easily type cp -r /bin as cp-r ~/bin. The flag itself does not prevent mistakes or necessarily make anyone better at being careful. If you wanted to prevent mistakes, the cp command could just as easily look at the node in question and provide a prompt, something along the lines of "This is a directory, do you want to copy all the contents to the specified location(y/n)?" That would be safety net. Requiring -r for directories keeps bloat of code down more than anything.
    – JDL
    Nov 13, 2014 at 21:12
  • @JDL Huh? How many users have a bin directory in their $HOME? (Half a percent?) Because this is what the tilde ~ actually stands for. So I don't see any sense in the beginning of your argumentation. cp would always complain if no $HOME/bin aka ~/bin existed. So there is no "safety concern" in this case. Nov 14, 2014 at 7:53
  • 7
    When we move a directory we do not need -r. I think the linked answer at unix.stackexchange.com is much more to the point. The equivalent of a non-recursive copy would be to have a second directory, and hard-links for all files inside the directory tree.
    – gerrit
    Nov 14, 2014 at 16:50
  • 2
    If I'm not mistaken, -r was a GNU extension - I don't think the historical UNIX cp had a recursive copy - and that was part of the reason for the command rsync.
    – Mei
    Nov 14, 2014 at 19:11
  • 2
    @JakeGould I understand basic concepts. I, however, argue against the idea that a -r flag on the command in question provides any additional safety. From my experience, command line linux commands are historically inherently unsafe. Safety has been added overtime, if at all. The safety inherent in linux design comes from the permissions system and forgoing running as root. Not running as root is also something that is more of a convention than a design. The convention is supported in most new linux installers, but was not always.
    – JDL
    Nov 14, 2014 at 22:30
20

It's very true that this is the behavior we want nearly all the time. This doesn't necessarily mean, though, that copying recursively should be the default behaviour.

I think the reasons cp acts as it does have roots in Unix philosophy. Unix favors programs that do one thing and do it well, as well as programs that are simple in both interface and implementation (sometimes called worse is better).

The key piece of the puzzle here is realizing that cp does not copy directories - cp copies files (and only files). If you want to copy a directory, cp calls itself recursively, in order to copy the files on each directory.

Of course, the difference between "copying directories" and "copying files recursively" is absolutely nothing, from a user's perspective, but having this interface helps the implementation remain simple.

If you made cp able to copy directories, you'd soon be tempted to add more features that only make sense for directories - for example, you might want to only copy filenames that ended in .sh. Inevitably, this leads to the bloat and feature creep that we are used to in other operating systems - making software slow, complex and error-prone.

Another advantage is that having -r also helps the user understand what is really happening underneath the interface. A nice side effect of this is that learning the concept of recursive operation will spare you some work when you learn about other tools that support it (such as grep, for example)


Some people will certainly tell you that exposing implementation details to the user is bad, and that having more features is good. My intent here is merely explaining the rationale of this behaviour, so I won't try to argue either way.

1
7

Interactions with directories makes sure you know you're interacting with a directory and NOT just a single file.

For instance:

$ tree
.
└── folder1
    └── sub1
        └── subsub1

3 directories, 0 files
$
$ cp folder1/ folder2
cp: folder1/ is a directory (not copied).
$
$ mkdir blah
$ cp blah/ blah2
cp: blah/ is a directory (not copied).
$ rm blah/
rm: blah/: is a directory

So, if you want to successful copy a folder, since it implies both the folder and the objects related to referencing the folder, you have to treat it as if its a collection of files:

$ cp -r folder1/ folder2
$ rm -rf folder1
5

A clear advantage of the -r flag is that you can cp * /target/dir to copy only all files in the source directory into the target directory, omitting (albeit with a warning) all directories contained therein. cp -r * /target/dir would copy everything instead, including subdirectories.

4

It may be suboptimal UI today, but it was a decision made some time around 1970 during the design of UNIX when disk was considerably more expensive. Millions of shell scripts depend on it working that way, and it's far too late to change it.

See this article for the original design info.

4

The consequence of changing the default would be that thousands of shell scripts would break. This leads to the POSIX and SUS requirements for the well known default behavior.

The reason is the historical development of cp, ln and mv commands (all the same binary on most old UNIX systems) in various UNIX branches. When -r appeared (early cp did not have an option to copy directories; here is an early cp man page without -r or -R), there were various differences in handling special files, symlinks and and other vagaries of the filesystem.

From The Open Group Base Specifications Issue 7:

Earlier versions of this standard included support for the -r option to copy file hierarchies. The -r option is historical practice on BSD and BSD-derived systems. This option is no longer specified by POSIX.1-2008 but may be present in some implementations. The -R option was added as a close synonym to the -r option, selected for consistency with all other options in this volume of POSIX.1-2008 that do recursive directory descent.

The difference between -R and the removed -r option is in the treatment by cp of file types other than regular and directory. It was implementation-defined how the - option treated special files to allow both historical implementations and those that chose to support -r with the same abilities as -R defined by this volume of POSIX.1-2008. The original -r flag, for historic reasons, did not handle special files any differently from regular files, but always read the file and copied its contents. This had obvious problems in the presence of special file types; for example, character devices, FIFOs, and sockets.

In fact you will still see some people regularly using:

cd dir1 ; tar -cf - . | (cd dir2 ; tar -xpf -)

Because they don't trust that the cp -r implementation with be what they are used to on an arbitrary machine; Or because they want the tar behavior.

2

You need this flag only when the cp is a command for copying files and directories and not only directories.

If there was a special command for copy of directories, the “default” behavior would be surely a recursive copy.

1
  • 1
    @JakeGould maybe because they may need to preserve ownership and permissions?
    – Ruslan
    Nov 14, 2014 at 18:30
2

As others have mentioned, a directory is basically just another type of file (as opposed to a regular file), which usually "contains" (points to) other files. It could contain subdirectories, for which the same applies...

So if you're copying a directory (user's perspective), you are really copying a bunch of files (filesystem's perspective) (regular files, directory files, symbolic links, ...) and for every directory file, you are recursively repeating that process. Since copying a directory is by definition a recursive process, cp's argument is called --recursive.

It is, of course, very easy to create a command shortcut in your user environment (put this in your .profile/.bashrc file to make it permanently available):

alias cpr='cp -r'

Or maybe better:

alias cpa='cp -av'

That way, you can copy a directory using cpa dir1 copyDir1 and it will not only print what's being copied but also apply file permissions.

And since someone mentioned that cp could theoretically detect that the source file is a directory and ask if it should copy it recursively, here's a quick suggestion:

cp()
{
    if [ ! -e "$1" ]; then
        echo missing source file
        return 1
    fi
    arg="-d --preserve=all -v"
    if [ -d "$1" ]; then
        read -p "Copy directory recursively? " -n 1 -r
        if [ "$REPLY" == "y" ]; then
            arg="$arg -r"
        fi
        echo
    fi
    /usr/bin/cp $arg "$@"
}

This is just a cheap cp wrapper. It always preserves all meta data (i.e., copies file modification time, properly copies symlinks and so on) and if you're trying to copy a directory, it asks if it should copy it (recursively).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .