Q: I have a huge directory of pictures.  Mixed in with the photo are video files with different extensions.  For example I have avi, mp4, mpg, etc…  I want to move all the video to a different folder for quick access.  But the problem is the everything is in dated folders and I would like keep them in the same folders after the move.  For example I have the following videos:

/home/Eric/Pictures/1-1-2013/newyears.mov
/home/Eric/Pictures/2012/12-25-2012/xmasparty.avi

I want to move them (along with many others) to the Videos folder and keep the directory structure like this:

/home/Eric/Videos/1-1-2013/newyears.mov
/home/Erix/Videos/2012/12-25-2012/xmasparty.avi

I have tried cp, mv, etc and even loops after someone suggested it, but keep running into the problem of directory does not exist.  I need to create all those directories, HELP!

A: I can understand your frustration.  There are several ways to go about this.

Using Find Command to Copy, Then Delete

The cp command has the --parents option that allows you to create all the "parent" directories when copying a file. We can use the find command to find the files, and the cp command to copy them. We can then go back and use the find command again, this time removing them from the source directory.

Find all of the video files in the /home/Eric/Pictures directory and copy them and the file structure to /home/Eric/Videos.

find /home/Eric/Pictures ( -iname ".mov" -o -iname ".mp4" -o -iname "*.avi" ) -exec cp -v --parents {} /home/Eric/Videos/ \;

Example:

$ find /home/Eric/Pictures ( -iname ".mov" -o -iname ".mp4" -o -iname "*.avi" ) -exec cp -v --parents {} /home/Eric/Videos/ \;
 /home -> /home/Eric/Videos/home
 /home/Eric -> /home/Eric/Videos/home/Eric
 /home/Eric/Pictures -> /home/Eric/Videos/home/Eric/Pictures
 /home/Eric/Pictures/2012 -> /home/Eric/Videos/home/Eric/Pictures/2012
 /home/Eric/Pictures/2012/12-25-2012 -> /home/Eric/Videos/home/Eric/Pictures/2012/12-25-2012
 '/home/Eric/Pictures/2012/12-25-2012/xmas.avi' -> '/home/Eric/Videos/home/Eric/Pictures/2012/12-25-2012/xmas.avi'
...OUTPUT TRUNCATED...

Now let's run a similar command, this time deleting the source files when we find them.

find /home/Eric/Pictures ( -iname ".mov" -o -iname ".mp4" -o -iname "*.avi" ) -exec rm -v {} \;

Example:

$ find /home/Eric/Pictures ( -iname ".mov" -o -iname ".mp4" -o -iname "*.avi" ) -exec rm -v {} \;
 removed '/home/Eric/Pictures/home/Eric/Videos/home/Eric/Pictures/2012/12-25-2012/xmas.avi'
 removed '/home/Eric/Pictures/home/Eric/Videos/home/Eric/Pictures/2012/12-25-2012/xmas.mp4'
 removed '/home/Eric/Pictures/home/Eric/Videos/home/Eric/Pictures/2013/7-4-2013/4th.mov'
 removed '/home/Eric/Pictures/home/Eric/Videos/home/Eric/Pictures/1-1-2013/testfile123.avi'
 removed '/home/Eric/Pictures/home/Eric/Videos/home/Eric/Pictures/1-1-2013/newyears.mov

Using rsync With A File List

This may be a little more complicated, but it may also be safer. Since you are generating a list of files, you can check them before taking any action. If you like you can also use the rsync command with the -n (--dry-run) option to test..

First, change into the working directory and create a text file of all the files you want to copy.

cd /home/Eric/Pictures
find . ( -iname ".mp4" -o -iname ".avi" -o -iname ".mov" -o -iname ".mpg" -o -iname "*.wmv" ) -print >> filelist.txt

Above we are using the find command to locate all of the video files by their extension, then redirecting the output to filelist.txt.

You will have a . (notating current directory) in front of all the filenames.  Let’s use sed to remove them.

sed -i -e 's/^.//g' filelist.txt

Now you should have a list of files you want to copy.  Use rsync with a few options to copy those files and delete the source files (same thing mv would do).

rsync --files-from=filelist.txt --remove-source-files /home/Eric/Pictures/ /home/Eric/Videos/

And that’s it…

Conclusion

I am sure someone, somewhere has a more elegant way of doing this (if so please leave it in comments).  

Resources and Links