102

I have two directories images and images2 with this structure in Linux:

/images/ad  
/images/fe  
/images/foo  

... and other 4000 folders

and the other is like:

/images2/ad  
/images2/fe  
/images2/foo

... and other 4000 folders

Each of these folders contain images and the directories' names under images and images2 are exactly the same, however their content is different. Then I want to know how I can copy-merge the images of /images2/ad into images/ad, the images of /images2/foo into images/foo and so on with all the 4000 folders..

  • 1
    are the end files named the same in both directories? – Simply_Me Aug 12 '14 at 23:06
  • Nope... for example in images/ad are 1.jpg, 2.jpg and 3.jpg. But in images2/ad are 4.jpg and 5.jpg – ssierral Aug 12 '14 at 23:09
  • 4
    @AmirAliAkbari, I don't think that it is a duplicate - the other question basically is 'Does mv do merging?' (answer: no). This question is about how to merge 2 directory hierarchies. – maxschlepzig Feb 7 '16 at 7:55
163

This is a job for rsync. There's no benefit to doing this manually with a shell loop unless you want to move the file rather than copy them.

rsync -a /path/to/source/ /path/to/destination

In your case:

rsync -a /images2/ /images/

(Note trailing slash on images2, otherwise it would copy to /images/images2.)

If images with the same name exist in both directories, the command above will overwrite /images/SOMEPATH/SOMEFILE with /images2/SOMEPATH/SOMEFILE. If you want to replace only older files, add the option -u. If you want to always keep the version in /images, add the option --ignore-existing.

If you want to move the files from /images2, with rsync, you can pass the option --remove-source-files. Then rsync copies all the files in turn, and removes each file when it's done. This is a lot slower than moving if the source and destination directories are on the same filesystem.

  • 14
    ..add -P if you'd like to see progress.. – Meetai.com Jun 16 '15 at 6:00
  • 1
    I would add that there's no benefit to using a tricky shell loop here even if you do want to move them instead of copying them—in that case just use rsync, then rm -r /images. – Wildcard Feb 7 '16 at 8:35
  • @Wildcard, well, that's not quite the same as moving. As Gilles points out, it's a lot slower than moving if they're on the same fs; and moreover it requires a lot more temporary spae. – LarsH Jun 29 '16 at 2:11
  • I'd also like to point out that it's important to include the trailing slashes for each directory. For example, if you simply ran rsync -a images images2, it will just copy images2 into images instead of merging them. – Kyle Challis Oct 18 '17 at 1:28
  • 1
    @s3cur3 Oh. From 2 to (implicit) 1. That's unintuitive. Thanks, I fixed my answer. – Gilles Jul 24 at 6:55
40

The best choice, as already posted, is of course rsync. Nevertheless also unison would be a great piece of software to do this job. Both can be used in several operating systems.

Rsync

rsync synchronizes in one direction from source to destination. Therefore the following statement

rsync -avh --progress Source Destination

syncs everything from Source to Destination. The merged folder resides in Destination.

-a means "archive" and copies everything recursively from source to destination preserving nearly everything.

-v gives more output ("verbose").

-h for human readable.

--progress to show how much work is done.

If you want only update the destination folder with newer files from source folder:

rsync -avhu --progress source destination

Unison

unison synchronizes in both directions. Therefore the following statement

unison Source Destination

syncs both directories in both directions and finally source equals destination. It's like doing rsync twice from source to dest and vice versa.

For more advanced usages look at the man pages or the following websites:

  1. https://www.cis.upenn.edu/~bcpierce/unison/
  2. https://rsync.samba.org/
  • 2
    I want to mention that the correct path to the folder should be with the trailing slash at the end rsync -avh --progress source/ destination/ , otherwise source folder will be created in destination folder, at least in my case that was like this. – electroid Oct 23 '16 at 7:12
  • This works great for me (with the trailing slash in folders). Thank you! – Leopoldo Sanczyk Nov 2 '16 at 22:30
5
for dir in images2/*; do mv "$dir"/* "images/$(basename "$dir")"; done

Loop over all the contents of images2 using an expanded glob (to avoid the problems with parsing ls) then mv the contents of those items to the matching entry in images. Uses basename to strip the leading images2 from the globbed path.

  • please add more details on what your command does so that it is useful for future readers as well :) – Ramesh Aug 12 '14 at 23:59
1

@inulinux12 , you can use the following one line for loop from command line:

$ for dir in images2/*; do mv "$dir"/* "${dir/2/}"; done

This will move all of the files from images2 to images in their respective directories. Note: this assumes no files have the same name.

For example:

Before execution:

$ ls -R images*
images:
ad  adfoo  fe
images/ad:
jpg.1  jpg.2
images/adfoo:
jpg.7
images/fe:
jpg.5
images2:
ad  adfoo  fe
images2/ad:
jpg.3
images2/adfoo:
jpg.6
images2/fe:
jpg.4

After execution:

$ ls -R images*
images:
ad  adfoo  fe
images/ad:
jpg.1  jpg.2  jpg.3
images/adfoo:
jpg.6  jpg.7
images/fe:
jpg.4  jpg.5
  • 3
    Don't parse the output of ls. mywiki.wooledge.org/ParsingLs – Etan Reisner Aug 12 '14 at 23:48
  • @EtanReisner Thank you for the suggestion; seems pretty narrow scenarios though given the information given in the question. – Simply_Me Aug 13 '14 at 0:01
  • 2
    @Simply_Me While true that it will usually be fine, you really don't want it to blow up when you hit a case you weren't counting on. It can cause really bad problems. Not to mention that it is quite often (as in this case) almost trivially replaceable with a simple glob. See my answer as an example of that. – Etan Reisner Aug 13 '14 at 0:04
  • @Simply_Me The scenarios include file names with spaces, which is pretty common for images. – Gilles Aug 13 '14 at 0:35
  • @Gilles and @Etan Reisner , thank you for the input, I appreciate it! Updated and tested my answer to use string substitution, and it came to be faster than using basename in this particular situation (no need to call basename for this). Thanks again for constructive comments. – Simply_Me Aug 13 '14 at 1:30

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.