239

I have a ton of files and dirs in a subdirectory I want to move to the parent directory. There are already some files and dirs in the target directory which need to be overwritten. Files that are only present in the target should be left untouched. Can I force mvto do that? It (mv * ..) complains

mv: cannot move `xyz' to `../xyz': Directory not empty

What am I missing?

EricSchaefer
  • 2,823

7 Answers7

181

You will have to copy them to the destination and then delete the source, using the commands cp -r * .. followed by rm -rf *.

I don't think you can "merge" directories using mv.

dogbane
  • 29,677
  • 12
    Well, that's what I didn't want to do, because it will take a long time... Thanks anyway. – EricSchaefer Mar 22 '11 at 17:45
  • 14
    Presumably mv is faster because you're on the same filesystem? What if you use cp -l to create hardlinks rather than actually moving the files? – mattdm Mar 22 '11 at 20:32
  • 8
    You should use cp -a instead of cp -r in order to preserve the file attributes (timestamp, permissions, etc.). – dotancohen Feb 02 '14 at 07:14
  • 9
    For people arriving here late via google, the answer below by @palswim emulates the behavior of mv by creating new hard links to the data and then deleting the old links. Short answer cp -rl source destination && rm -r source. – William Everett May 31 '16 at 17:43
  • 5
    Always BE CAREFUL when using rm -rf, be sure to specify the folder name at the end and to not use variables if you are in script, you might end up running rm -rf / and this will "kind" break your server. – Evis Jul 07 '21 at 14:24
  • @elvis meaning: always have backups! – cregox Jul 30 '22 at 01:54
100

rsync would probably be a better option here. It's as simple as rsync -a subdir/ ./.

My test tree in filename:contents format:

./file1:root
./file2:root
./dir/file3:dir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Running rsync:

$ rsync -a -v subdir/ ./
sending incremental file list
./
file1
dir/
dir/file3

Gives:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

And then, to emulate mv, you probably want to remove the source directory:

$ rm -r subdir/

Giving:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir

If this is wrong, can you please provide a similar example (e.g. using my test tree from near the top of this answer) with the desired result?

jII
  • 359
Mikel
  • 57,299
  • 15
  • 134
  • 153
  • 7
    rsync copies. This question is about moving. – Gilles 'SO- stop being evil' Mar 22 '11 at 22:51
  • 3
    @Gilles: Thanks. I added rm -r at the end to make it basically the same as mv. – Mikel Mar 22 '11 at 22:53
  • 9
    copy-then-delete isn't equivalent to mv when the source and destination are on the same filesystem. mv is atomic, preserves inode numbers (so the file can remain open), and doesn't take time and space making a copy. – Gilles 'SO- stop being evil' Mar 22 '11 at 23:08
  • 9
    @Gilles: I realize that, but currently the leading answer is cp -r; rm -r. I think in that sense, rsync is worth mentioning too. – Mikel Mar 22 '11 at 23:21
  • 1
    I already did it with cp/rm (it was urgent). It really took a long time. Gilles script would probably have been a lot quicker, but he was too late too. – EricSchaefer Mar 23 '11 at 10:09
  • +1 did the job for me. You may want to run "diff -rq source_dir dest_dir" before proceeding with the rsync. – MiniQuark Nov 30 '11 at 12:33
  • 1
    this is a much better way, the other way you have to agree to overwrite every file, on centos 5.9 anyway, for some reason -f doesn't solve that :/ – Joshua D'Alton Jul 27 '13 at 06:12
  • 3
    This is totally incorrect. rsync as you have shown it is an incremental copy. You need to add the --delete, option. – RichieHH Aug 21 '20 at 06:15
73

rsync can delete the source after copying with the --remove-source-files parameter.

From the rsync man page:

--remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
  • 731
  • 5
    This is really the best answer. I couldn't use cp -r; rm because of lack of free space. Instead rsync --remove-source-files both minimized used disk space avoided copying over the exact same files. – the Aug 05 '14 at 12:10
  • Maybe --link-dest to. – user1133275 Jan 31 '20 at 09:47
  • 2
    That leaves empty folders in place. Is there a flag to get rid of empty folders? – rustyx Jun 03 '21 at 14:33
42

You can do this with cp and rm, but without copying the massive amount of data you are (presumably) trying to avoid transferring. @mattdm alluded to this in his comment, and an answer for another question has a more complete discussion about various options.

cp -rlf source destination
rm -r source

Essentially, the -l option for the cp command creates hard links to files rather than copying their data to new files.

palswim
  • 5,167
  • 3
    I know the OP already completed his task which prompted the question, but hopefully this answer can help anyone with this problem in the future. – palswim Jan 31 '15 at 07:36
  • 1
    This really the answer they needed. I just did this with 60GB of thousands of small cyrus email files and it took only 21 seconds. – labradort May 12 '15 at 18:37
  • 2
    This is also the route I took - I just changed it to cp -al source destination to preserve owner info and permissions. – piit79 Nov 20 '15 at 09:32
  • I think to actually do what OP asked for you have to add the -f option otherwise it will not overwrite if the file exists. Can you confirm that or am I doing something wrong? – das Keks Jan 17 '18 at 23:40
  • @dasKeks: Actually, cp overwrites by default. The -f option attempts to remove the file (as in rm) before trying to copy anew, which can help if the process can't open the file for writing, though some people would prefer to see that there was an error instead. I don't know if it mattered to the OP, but I added the -f flag to my answer anyway. – palswim Jan 18 '18 at 00:53
  • should probably use cp -ralf as default. but only works where hardlinks are accepted, obviously! sadly, not in termux – cregox Jul 30 '22 at 01:53
10

Here's a script that moves files from under /path/to/source/root to the corresponding path under /path/to/destination/root.

  • If a directory exists in both the source and the destination, the contents are moved-and-merged recursively.
  • If a file or directory exists in the source but not in the destination, it is moved.
  • Any file or directory that already exists in the destination is left behind. (In particular merged directories are left behind in the source. This is not easy to fix.)

Beware, untested code.

export dest='/path/to/destination/root'
cd /path/to/source/root
find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' {} \; -o \
                  -exec sh -c 'mv "$0" "$dest/$0"' {} \; -prune \) \
    -o -exec sh -c '
        if ! [ -e "$dest/$0" ]; then
          mv -f "$0" "$dest/$0";
        fi
' {} \;
  • Two corrections: you need a \; before the -o on the first line of the find command, and you shouldn't escape ! in the if -- it's just !, not \! – llhuii Apr 20 '11 at 05:30
  • Does not work - I get: mv: cannot move '.' to '//./.': Device or resource busy. I would like to move stuff from /a/b to /a – ESP32 Jan 30 '20 at 21:43
  • @Gerfried I just fixed a mistake in the code that skips existing directories. Please try again. – Gilles 'SO- stop being evil' Jan 31 '20 at 09:22
4

This thread is out there for years and still ranks #1 on google so i wanted to add another method. How i usually do this: packing the subdir content into a tarball, moving the tarball up to the parent directory and then extract it with the default --overwrite behaviour. This does exactly what you're looking for. Afterwards you can remove your subdir.

cd xyz
tar -cvzpf tmp.tar.gz *
mv tmp.tar.gz ../tmp.tar.gz
cd ..
tar -xvzpf tmp.tar.gz
rm -rf xyz
rm -f tmp.tar.gz
  • 3
    This only works if you have the extra space for the compressed tar file. And do you really want to keep the temporary tar file around after extraction? – Anthon Apr 13 '16 at 09:05
  • 1
    Edited code to remove tmp file. Nowadays the space isn't the problem in the most cases. #terabyteages – Simon Kraus Apr 13 '16 at 09:22
0

If you have enough storage you can do it the following way:

mv -bfv directory_1/* directory_2/ # all duplicate source files/directories 
                                   # will have ~ appended to them
find -name "*~" -delete            # will recursively find and delete all files 
                                   # with ~ on the end

Make sure there aren't any important files with a ~ on the end of them, but if there are you can add --suffix=whateveryouwant instead of the default.

Anthon
  • 79,293
4D3H2
  • 1