Find and move directories based on file type and date

5,440

Do not -exec mv the directory which is currently being examined by find. It seems that find gets confused when you do that.

Workaround: first find the directories, then move them.

cd "/mnt/user/New Movies/"
find -type f \( -name "*.avi" -or -name ".*mkv" \) -mtime +180 \
  -printf "%h\0" | xargs -0 mv -t /mnt/user/Movies

Explanation:

  • -printf prints the match according to the format string.
  • %h prints the path part of the match. This corresponds to the "${0%/*}" in your command.
  • \0 separates the items using the null character. This is just precaution in case the filenames contain newlines.
  • xargs collects the input from the pipe and then executes its arguments with the input appended.
  • -0 tells xargs to expect the input to be null separated instead of newline separated.
  • mv -t <target> allows mv to be called with all the source arguments appended at the end.

Note that this is still not absolutely safe. Some freak scheduler timing in combination with pipe buffers might still cause the mv to be executed before find moved out of the directory. To prevent even that you can do it like this:

cd "/mnt/user/New Movies/"
find -type f \( -name "*.avi" -or -name ".*mkv" \) -mtime +180 \
  -printf "%h\0" > dirstomove
xargs -0 mv -t /mnt/user/Movies < dirstomove

Background explanation:

I asume what happens with your find is following:

  1. find traverses the directory /mnt/user/New Movies/. While there it takes note of the available directories in its cache.
  2. find traverses into one of the subdirectories using the system call chdir(subdirname).
  3. Inside find finds a movie file which passes the filters.
  4. find executes mv with the given parameters.
  5. mv moves the directory to /mnt/user/Movies.
  6. find goes back to parent directory using the system call chdir(..), which now points to /mnt/user/Movies instead of /mnt/user/New Movies/
  7. find is confused because it does not find the directories it noted earlier and throws up a lot of errors.

This assumption is based on the answer to this question: find -exec mv stops after first exec. I do not know why find just stops working in that case and throws up errors in your case. Different versions of find might be the explanation.

Share:
5,440

Related videos on Youtube

Sudesh Gama
Author by

Sudesh Gama

Updated on September 18, 2022

Comments

  • Sudesh Gama
    Sudesh Gama over 1 year

    I have a directory "New Movies" containing 200+ subdirectories "Movie Name (year)". The subdirectories "Movie Name (year)" contains a single video file (avi/mkv) and several related jpg/xml files.

    I need to move all directories containing a video file that is at least 180 days old to another directory.

    For example:

    New Movies/Movie A (year) would contain movie.mkv, folder.jpg & movie.xml and I want to move them to a subdirectory of the same name elsewhere with all files present and intact.

    After looking elsewhere on this site I have tried:

    cd "/mnt/user/New Movies/"
    find -type f \( -name "*.avi" -or -name ".*mkv" \) -mtime +180 -exec sh -c 'mv "${0%/*}" /mnt/user/Movies' {} \;
    

    The command successfully moves the first subdirectory and all files to their new home, however every single operation after this one returns the error

    find: './Movie B (year)': No such file or directory
    find: './Movie C (year)': No such file or directory
    

    and so on through all directories contained under "New Movies" whether they were due to be moved or not.