179

I'm trying to write a Bash script that will overwrite an existing directory. I have a directory foo/ and I am trying to overwrite bar/ with it. But when I do this:

cp -Rf foo/ bar/

a new bar/foo/ directory is created. I don't want that. There are two files in foo/; a and b. There are files with same names in bar/ as well. I want the foo/a and foo/b to replace bar/a and bar/b.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
saketrp
  • 2,323
  • 3
  • 16
  • 18

10 Answers10

179

You can do this using -T option in cp.
See Man page for cp.

-T, --no-target-directory
    treat DEST as a normal file

So as per your example, following is the file structure.

$ tree test
test
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

You can see the clear difference when you use -v for Verbose.
When you use just -R option.

$ cp -Rv foo/ bar/
`foo/' -> `bar/foo'
`foo/b' -> `bar/foo/b'
`foo/a' -> `bar/foo/a'
 $ tree
 |-- bar
 |   |-- a
 |   |-- b
 |   `-- foo
 |       |-- a
 |       `-- b
 `-- foo
     |-- a
     `-- b
3 directories, 6 files

When you use the option -T it overwrites the contents, treating the destination like a normal file and not directory.

$ cp -TRv foo/ bar/
`foo/b' -> `bar/b'
`foo/a' -> `bar/a'

$ tree
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

This should solve your problem.

Saurabh Meshram
  • 8,106
  • 3
  • 23
  • 32
  • 29
    just in case anyone is tripped up by this it won't work with OSX cp https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/cp.1.html – dnfehren Mar 18 '16 at 19:07
  • 18
    It's not clear that this answer is what the OP is looking for, although the examples given above mask the problem... With the -T option, files that are in an existing target (`bar/`) but *not* in the source (`foo/`) will be left in place, so this is not what most people would consider a complete overwrite of the directory. ie. if `bar/baz` existed already, it would still exist afterwards... – robo Dec 19 '16 at 21:40
  • Is there no option to clean target folder in the process? I can't believe it! – JohnyTex Sep 27 '17 at 11:11
  • 5
    This answer does answer the op question, but doesn't address the case of if the destination already exists and you want to remove contents it contains but the source directory does not. This is not an expected behavior of copying files from one place to the other. It only overwrites in the target things that are also in the source, it doesn't touch anything in the target that is not in the source. You can clean the target folder by prepending a command to do it: `rm -rf bar/* && cp -TRv foo/ bar/` – theferrit32 Dec 14 '17 at 21:08
  • 3
    I'm not a mind reader...I don't see further clarification what the OP was looking for, but this was DEFINITELY the answer that I (MEEEE) was looking for – Assimilater Feb 03 '19 at 05:23
  • 2
    just in case anyone is tripped up by why the link in the most upvoted comment is not working, here it is: https://web.archive.org/web/20170909193852/https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/cp.1.html – Sulphur Feb 27 '20 at 21:14
  • -T is a gnuism. I believe the BSDism is to put a slash / after the src directory. I'm not sure where MacOS gets its convention or what the relevant standards have to say, but it seems to follow BSD here. – xpusostomos Aug 05 '20 at 13:15
106

If you want to ensure bar/ ends up identical to foo/, use rsync instead:

rsync -a --delete foo/ bar/

If just a few things have changed, this will execute much faster than removing and re-copying the whole directory.

  • -a is 'archive mode', which copies faithfully files in foo/ to bar/
  • --delete removes extra files not in foo/ from bar/ as well, ensuring bar/ ends up identical
  • If you want to see what it's doing, add -vh for verbose and human-readable
  • Note: the slash after foo is required, otherwise rsync will copy foo/ to bar/foo/ rather than overwriting bar/ itself.
    • (Slashes after directories in rsync are confusing; if you're interested, here's the scoop. They tell rsync to refer to the contents of the directory, rather than the directory itself. So to overwrite from the contents of foo/ onto the contents of bar/, we use a slash on both. It's confusing because it won't work as expected with a slash on neither, though; rsync sneakily always interprets the destination path as though it has a slash, even though it honors an absence of a slash on the source path. So we need a slash on the source path to make it match the auto-added slash on the destination path, if we want to copy the contents of foo/ into bar/, rather than the directory foo/ itself landing into bar/ as bar/foo.)

rsync is very powerful and useful, if you're curious look around for what else it can do (such as copying over ssh).

Tom Potter
  • 1,161
  • 1
  • 7
  • 4
82

Do it in two steps.

rm -r bar/
cp -r foo/ bar/
Jonathan Wheeler
  • 2,539
  • 1
  • 19
  • 29
  • 16
    This is actually the **only** example given so far that will ensure that `bar` is identical in content to `foo`, not a combination of items from `foo` plus other items that may have already existed in `bar`. The highly upvoted answer from @Saurabh Meshram below has this problem. – robo Dec 19 '16 at 21:43
  • 1
    Be extra careful using this, as it *will* remove all files from bar, even hidden ones. – Elia Grady Jul 13 '17 at 08:20
  • 1
    mac osx: ```rm -rf bar/; cp -r foo/ !$``` – Michael Dimmitt Sep 28 '17 at 11:44
  • 2
    This isn't always viable...or desired...imagine if `foo` and `bar` are large directories...maybe there are files in `bar` that are not in `foo` that you don't want to remove. This shouldn't be considered a realistic answer imho – Assimilater Feb 03 '19 at 05:21
  • Although worked for me, but not the best solution since there might be file included in foo/ but not in bar/ which will be deleted after doing this. – Nitwit May 02 '20 at 15:45
  • @robo and also the rsync answer now – qwr Jul 13 '20 at 03:08
  • @Jonathan Is the end result same as the answer given above: https://stackoverflow.com/a/53349667/3503228 – Porcupine Feb 07 '21 at 09:41
  • @Porcupine The names and contents of the files should be the same, but I think that the permissions and owners will be different between the cp and rsync methods. The timestamps may also be different. – Jonathan Wheeler Feb 08 '21 at 14:38
  • @JonathanWheeler So, which one of rsync and cp, you think, does a better job in preserving those attributes? – Porcupine Feb 09 '21 at 07:58
  • 2
    @Porcupine rsync – Jonathan Wheeler Feb 10 '21 at 13:29
31

Use this cp command:

cp -Rf foo/* bar/
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 13
    This doesn't remove files that are present in bar but not in foo. – Ara Apr 01 '15 at 15:00
  • 10
    Don't know what you mean by that. Why should `cp` command be removing file from source? – anubhava Apr 01 '15 at 15:21
  • My understanding was that when you 'overwrite an existing directory' with another one, at the end the overwritten directory should be a copy of the other one. I.e. at the end bar should be a copy of foo, as it is the case with @jonathan-wheeler answer but if you had a file bar/c and no foo/c then bar/c doesn't get deleted. On a side note, I've just noticed that it's not the case with Saurabh Meshram's answer either. – Ara Apr 03 '15 at 15:52
  • Maybe we don't agree on what overwrite means, for me it's basically replace, while you might meant megre ? It's hard to tell what OP wants exactly because she only mentionned 2 files that are both in foo and bar. – Ara Apr 03 '15 at 16:02
  • 2
    This syntax also ignores hidden dot files. A large number can also blow up the glob. – xpusostomos Aug 05 '20 at 13:20
  • That can be controlled using `shopt` – anubhava Aug 05 '20 at 13:41
18

The following command ensures dotfiles (hidden files) are included in the copy:

$ cp -Rf foo/. bar
May Oakes
  • 4,359
  • 5
  • 44
  • 51
7

Very similar to @Jonathan Wheeler:

If you do not want to remember, but not rewrite bar:

rm -r bar/
cp -r foo/ !$

!$ displays the last argument of your previous command.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael Dimmitt
  • 826
  • 10
  • 23
3

this should solve your problem.

\cp -rf foo/* bar/
ali
  • 180
  • 1
  • 9
0

In case you're in Git Bash for Windows:

robocopy path/to/source path/to/target -MIR

-MIR means mirror, it'll result in two identical folders. Existing files will be skipped, extra files will be deleted.

ZYinMD
  • 3,602
  • 1
  • 23
  • 32
-1

The operation you defined is a "merge" and you cannot do that with cp. However, if you are not looking for merging and ok to lose the folder bar then you can simply rm -rf bar to delete the folder and then mv foo bar to rename it. This will not take any time as both operations are done by file pointers, not file contents.

Ati
  • 1
  • 1
-6

Try to use this command:

cp -Rf foo/* bar/
Yahor M
  • 617
  • 8
  • 8