How do you copy directories in Linux in these situations using cp?

4

1

Situation 1

How do I copy /home/src/somedir to /dest/ such that the path always ends up as /dest/somedir, regardless of whether /dest/somedir already exists?

I want to avoid ending up with /dest/somedir/somedir.

Situation 2

How do I copy everything inside /home/src/somedir to /dest/ such that /dest/somedir isn't created and is ignored if it already exists?


Are there rules about the trailing slashes I can use? I thought ending with a trailing slash meant always copy the contents only but it still often goes wrong for me.

Person

Posted 2015-12-22T17:29:43.087

Reputation: 143

1what specifically do you mean by "ignored"? do you want the copy to continue or to abort? cp -r /home/src/somedir /dest/ should address both situations, either creating or merging into the existing folder – Frank Thomas – 2015-12-22T17:58:40.760

Does it have to be with cp? There's probably a correct option to rsync that will do this. – Barmar – 2015-12-25T18:55:06.047

Answers

7

cp will always copy the file(s) at the start of the command to the file or directory at the end of the command. The slash doesn't really do much to the arguments, unless the argument is a symlink to a directory. Then having the slash will treat it like a directory while omitting the slash will copy the link itself.

Assuming you want recursively copy any subdirectories in your examples you'd do them like:

cp -r /home/src/somedir /dest

and

cp -r /home/src/somedir/* /dest

The first gets the directory /home/src/somedir and will copy that argument, the directory itself and all the contents of it, to the last arg, /dest. It will create the somedir directory in /dest if needed and use it if it already exists.

The second addes * to the end of the first argument, which the shell will expand to be every file that does not start with . in /home/src/somedir and will copy all of those files and directories to /dest without regard to what's already there (except some flags to cp will make it prompt to overwrite files that will have the same in in the destination as an existing file).

As the comments to this answer have pointed out, there are problems using * to grab all the files in the directory. One alternative would be to use tar to do the copy for you

tar -c -C /home/src/somedir . | tar -x -C /dest

this will create a tar file of somedir without the leading path by using -C to switch to that directory first. By default tar will print to stdout which we will then pipe to another tar to extract it switching into the desired /dest directory first. This will also preserve lots of file attributes, which cp can do as well.

Eric Renouf

Posted 2015-12-22T17:29:43.087

Reputation: 1 548

2Worth noting that * wildcard expansion will fail if there are too many matches. I don't know the exact number, but the last time it failed for me, the directory contained around 100000 files/directories. – Aleks G – 2015-12-22T22:02:32.687

Good point, also if there are no files by default it will try to copy a file named * – Eric Renouf – 2015-12-22T22:11:35.467

Indeed, so in a production-level script you'd add all these checks. – Aleks G – 2015-12-22T22:16:09.210

Yep definitely want to avoid wildcard considering it doesn't copy hidden files and has issues in the way it expands in the shell. – Person – 2015-12-23T11:01:06.943

@AleksG and @Person given the observations of difficulties with globs I included an alternative that doesn't use cp but should accomplish the task – Eric Renouf – 2015-12-23T14:48:15.520

Can't you use a trailing dot with cp, e.g. /dir/. or do I misunderstand? – Person – 2015-12-23T14:51:26.147

@Person you could do that as long as you use -r with it. I like the tar trick though, because you can have the extract side on the other end of an ssh pipe to copy to a remote server that doesn't have the scp subsystem enabled for example – Eric Renouf – 2015-12-23T14:54:52.247

2

How do I copy /home/src/somedir to /dest/ such that the path always ends up as /dest/somedir, regardless of whether /dest/somedir already exists?

cp -rv /home/src/somedir /dest

We are copying «somedir» to «/dest», so we end up with «/dest/somedir»

How do I copy everything inside /home/src/somedir to /dest/ such that /dest/somedir isn't created and is ignored if it already exists?

cp -rv /home/src/somedir/* /dest

We are copying «all files in somedir» (somedir/*) to «/dest»

Caveat: somedir/* doesn't include hidden files (those beginning with a dot).

The final slashes don't affect the process. You may be confusing cp with rsync, where a trailing slash means “copy the contents, not the folder”.

Ángel

Posted 2015-12-22T17:29:43.087

Reputation: 960

I think this answer is missing an explanation about using a trailing dot to achieve copying the files. – Person – 2015-12-23T14:50:04.380