Copying all files of a directory, including hidden ones, with cp
I outline my exploration of why cp -R src/. dest copies contents of src – that too all its files, including hidden ones – and not src itself, as src/. had initially led me to believe. This is because of how the algorithm of cp is defined.
Table of contents
There are two directories - a non-empty src and an empty dest. My aim is to copy all the files in src inside dest, including hidden files.
$ ls -a src. .. .hidden unhidden$ ls -a dest. ..
src dir contains a normal file named unhidden and a hidden file called .hidden
Solution - cp -R src/. dest
I want to cut to the chase, so here is the command: cp -R src/. dest.
$ cp -R src/. dest$ ls -a dest. .. .hidden unhidden
src/. construct performs the magic.
I was more interested in figuring out why src/. makes this happen. It felt counter-intuitive to me – src/. simply refers to the . entry in the src directory, i.e., it refers to src itself. A command like cp -R src dest should have copied the entire src directory inside dest, ending up with a structure like dest/src/{.hidden,unhidden} instead of dest/{.hidden,unhidden}.
Why does it work?
After some faffing about, I found the algorithm used by cp as documented in POSIX. Below is the relevant portion of the algorithm.
cp -R source_file targetIf
targetexists and names an existing directory, the name of the corresponding destination path for each file in the file hierarchy rooted in each source_file shall be the concatenation oftarget, a single >slash< character iftargetdid not end in a >slash<, and the pathname of the file relative to the directory containingsource_file. [Emphasis added]
I tried to understand the algorithm by building path for src/.hidden file. According to the algorithm, the path of a target file is a concatenation of:
-
target (
destin our case) + - / if needed (which we do) +
-
relative pathname of the target file, starting from the directory that contains the
source_file. I am interpreting heavily now – in our case ofsrc/., the directory containing.issrc. So the pathname for the file.hidden, relative fromsrc, would become:./.hidden.
Thus, the built path for the hidden file .hidden becomes: dest/./.hidden, which is equal to dest/.hidden.
Similarly, for the normal file unhidden, the built path becomes dest/./unhidden, which is equal to dest/unhidden.
Written by Jayesh Bhoot