44

When I use

cp -R inputFolder outputFolder

the result is context-dependent:

  1. if outputFolder does not exist, it will be created, and the cloned folder path will be outputFolder.
  2. if outputFolder exists, then the clone created will be outputFolder/inputFolder

This is horrible, because I want to create some installation script, and if the user runs it twice by mistake, he will have outputFolder created the first time, then on the second run all the stuff will be created once again in outputFolder/inputFolder.

  • I want always the first behavior: create a clone next to the original (as a sibling).
  • I want to use cp to be portable (e.g. MINGW does not have rsync shipped)
  • I checked cp -R --parents but this recreates the path all the way up the directory tree (so the clone will not be outputFolder but some/path/outputFolder)
  • --remove-destination or --update in case 2 do not change anything, still things are copied into outputFolder/inputFolder

Is there a way to do this without first checking for existence of the outputFolder (if folder does not exist then...) or using rm -rf outputFolder?

What is the agreed, portable UNIX way of doing that?

  • 2
    Some of those options aren't POSIX, so the ones you tried aren't very portable. If you can live with a little lack of portability, why not use rsync? – muru Sep 9 '15 at 22:47
  • 1
    If you don't need to use cp, piping between two tar commands is a nice reliable way to copy trees. – R.. Sep 10 '15 at 3:39
  • @R.. can you provide an example? @muru I'd like this to work in MINGW which does not have rsync shipped. – jakub.g Sep 10 '15 at 8:21
  • With GNU tar, tar -C input -cf - . | tar -C output -xf - works. -C changes working directory, but it is not a standard option, so if it's not supported you need to run the two tars in the right working directories to begin with, e.g. ( cd input && tar -cf - . ) | ( cd output && tar -xf - ) However if you're dealing with Windows I would just use roaima's answer. – R.. Sep 10 '15 at 14:36
52

Use this instead:

cp -R inputFolder/. outputFolder

This works in exactly the same way that, say, cp -R aaa/bbb ccc works: if ccc doesn't exist then it's created as a copy of bbb and its contents; but if ccc already exists then ccc/bbb is created as the copy of bbb and its contents.

For almost any instance of bbb this gives the undesirable behaviour that you noted in your Question. However, in this specific situation the bbb is just ., so aaa/bbb is really aaa/., which in turn is really just aaa but by another name. So we have these two scenarios:

  1. ccc does not exist:

    The command cp -R aaa/. ccc means "create ccc and copy the contents of aaa/. into ccc/., i.e. copy aaa into ccc.

  2. ccc does exist:

    The command cp -R aaa/. ccc means "copy the contents of aaa/. into ccc/., i.e. copy aaa into ccc.

  • 3
    Huh, that's a new one for me, but it does seem to work! It this documented anywhere? – Ilmari Karonen Sep 9 '15 at 20:34
  • 1
    I've tested the inputFolder/. instead of inputFolder and indeed it works for me on Windows/Git Bash. (It does not work however with just a trailing /, I need also the dot after the slash). I gave +1 since it's interesting, though it's probably a bit too tricky to use it in public code :) – jakub.g Sep 9 '15 at 21:42
  • @IlmariJaronen it doesn't need to be documented explicitly. Follow through the explanation and you'll see how it simply "follows the rules". – roaima Feb 9 at 11:43
19

Don't copy the folder, only copy the contents:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

This way you both ensure that the target directory is created the first time the script runs and avoid the issue when running it a second time.

Chris Down very correctly points out that in bash, this will skip files whose name starts with a .. To avoid this, you can run shopt -s dotglob before running the command above.

Both -p for mkdir and -R for cp are defined by POSIX so this should be perfectly portable.

  • 6
    Note: this won't copy files starting with .. In bash, you can use dotglob for that. – Chris Down Sep 9 '15 at 17:38
  • 1
    Note this method is likely to fail if the number of directory entries in inputfolder is large, because the glob expansion could exceed the maximum command line length. – Tim Cutts Sep 23 '15 at 5:44
11

Try the -T option to cp. This exists in GNU coreutils cp version 8.22; it may not be portable outside that.

  • 1
    Yeah, it seems that this is exactly what I wanted: cp -T (cp --no-target-directory). unix.stackexchange.com/questions/94831/… Unfortunately my version of cp is much older. – jakub.g Sep 9 '15 at 16:20
  • 1
    +1 Used this just today. -u is a great option to add to make it lazy. – PSkocik Sep 10 '15 at 19:22
4

You could also use rsync:

rsync -uav inputFolder/ outputFolder/

(note the slashes, especially after the first one)

0

In my opinion, there is nothing simpler and more portable than rm -rf outputFolder. So, I always stick with that. I understand that your question is different, but I think that this is the best practice.

  • That fails if there were specific permissions or ownerships on the target that needed to be kept. Or if it was a symlink. – roaima Nov 2 '15 at 0:11
  • 1
    The question wants the result of cp to be the same, both when the directory does not exist and when it exists. So, what are you talking about? – dashohoxha Nov 2 '15 at 6:58
  • The cp command as given by the OP doesn't keep permissions (or timestamps). – roaima May 9 '16 at 6:36
0

You can use -t option of cp command as:

cp -R inputFolder -t outputFolder

now if target folder does not exist it will throw an error:

cp: failed to access ‘outputFolder’: No such file or directory

note above command will copy inputFolder along with its content (and not just contents inside it)

if you want to copy just the contents of inputFolder it gets bit tricky (since you have to be careful while using shell globbing while using asterisk *)

cp -R  -t outputFolder/ -- inputFolder/*

now if target folder does not exist it will throw an error:

cp: failed to access ‘outputFolder’: No such file or directory

works with cp (GNU coreutils) 8.23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.