I recently needed to move a large number of files (millions) in a deep directory structure, into another similar directory structure, “merging” the contents of each directory and creating any missing directories. This task is easily (though slowly) performed on Windows with Control-C Control-V in Explorer, but I could find no obvious way to do it on Linux.
There is quite a bit of discussion about this on the web, including:
- Suggestions to do with with tar; this is a poor idea because it copies all the file data, taking an enormously long time.
- Suggestions to do it with “mv -r”… but as far as I can tell, mv does not have a -r option.
After a little thought I came up with the script below. I’d love to have a Linux/Bash guru out there point out how awful it and and send me something better!
A critical feature for me is that it does not overwrite files; if a source file name/path overlaps a destinate file, the source file is left alone, untouched. This can be changed easily to overwrite instead: remove the [[-f]] test.
$ cat ~/bin/move_files_merge.sh #!/bin/bash # Move files from dir $1 to dir $2, # merging in to existing dirs # Call it like so: # move_files_merge.sh /FROM/directory /TO/directory # Lousy error handling: # Exit if called with missing params. [ "A" == "A${1}" ] && exit 1 [ "A" == "A${2}" ] && exit 1 echo finding all the source directories cd $1 find . -type d -not -empty | sort | uniq >$2/dirlist.txt echo making all the destination directories cd $2 wc -l dirlist.txt xargs --no-run-if-empty -a dirlist.txt mkdir -p rm dirlist.txt echo Moving the files cd $1 # There is surely a better way to do this: find . -type f -printf "[[ -f '$2/%p' ]] || mv '%p' '$2/%h'\n" | bash echo removing empty source dirs find $1 -depth -type d -empty -delete echo done.
I think konqueror behaves the same way as the windows explorer in that case.
Won’t rsync do the job for you?
Something like
rsync -avh /dir1 /dir2
Granted, this will overwrite older files with newer files. You can add “–ignore-existing” to the options to override that behavior.
rsync –ignore-existing -avh /dir1 /dir2