2

If I have the tree:

.
├── child
├── dir1
├── dir2
├── dir3
├── file1
├── file2
└── file3

This is the result of issuing these commands within child: This works and copies all files and directories:

cp -r ../* .
----
cp: cannot copy a directory, '../child', into itself, './child'

This only copies child and the files, but none of the directories:

cp -r .. .
----
cp: cannot copy a directory, '..', into itself, '.'
mcp
  • 717

1 Answers1

5

Have a look at the commands as they are actually executed:

$ set -x; cp -r ../* .
+ cp -r ../child ../dir1 ../dir2 ../dir3 ../file1 ../file2 ../file3 .
cp: cannot copy a directory, '../child', into itself, './child'
$ set -x; cp -r .. .
+ cp -r .. .
cp: cannot copy a directory, '..', into itself, '.'

Both of your commands attempt to copy a directory into itself, causing a loop. A cp implementation may detect it, and stop at some point, or keep copying indefinitely (for instance, GNU and BusyBox cp stops; FreeBSD cp keeps copying until it hits the file name length limit; similarly, cp on MacOS keeps copying, as this other question on U&L seems to suggest).
In any case, recursively copying a directory into itself will eventually stop with an error.

Assuming the question is about GNU cp (the one you likely have on a GNU/Linux system), in the general case your second command (cp -r .. .) will copy (an unpredictable?) part of the tree rooted in .., detect a loop and stop with an error, without any further processing of anything in that tree.

On the other hand, in your first command (cp -r ../* .), cp is given eight separate arguments. While the copy of child's content into child fails, the other directories and files are copied without issues because none of them contains child itself.
Note, however, that this won't "work" as intended on systems whose cp doesn't detect loops and keeps recursively copying.


As a safer approach, to copy all the content of the parent directory into the current directory (child), excluding the current directory itself, you may use:

$ shopt -s nullglob dotglob extglob
$ cp -r ../!(child) .
fra-san
  • 10,205
  • 2
  • 22
  • 43
  • No, it doesn't immediately abort (cp (GNU coreutils) 8.26). In some cases it copies some of the files from the source before stopping. It looks to me like it depends on the order it gets the file entries from the OS, i.e. if it gets a b child x y, then it copies a and b and stops on child. On the other hand, Busybox's cp stops immediately with cp: '..' and './..' are the same file – ilkkachu Jun 26 '20 at 21:46
  • @ilkkachu Thanks! Edited. – fra-san Jun 26 '20 at 22:29
  • So is the main point that cp -r .. . only has one argument .. and when it hits child it stops, whereas cp -r ../* . expands to multiple arguments so if cp fails on the first argument it moves onto the next? – mcp Jul 03 '20 at 13:08
  • Can you clarify this, "having nothing else to copy at the same level of .. or above it, stop with an error."? We're talking same level as the parent folder .. correct? For which we've provided no arguments. As opposed to same level of the contents in ... – mcp Jul 03 '20 at 13:08
  • 1
  • Yes. AFAICT GNU cp stops copying the tree rooted in one of the command line arguments as soon as an error occurs, then it moves to the next argument (I've given up trying to understand whether this is what POSIX requires). 2) I used that wording based on my initial (mis)read of the standard; what actually matters/happens is point (1) - cp stops copying the tree rooted in .. and, being it the only argument, it does nothing more; I think I'll eventually edit that bit to remove the unclear part.
  • – fra-san Jul 03 '20 at 15:34