Basically I want to do this:

mv /path/sourcefolder/* /path/destinationfolder/

but only with files older than eg 90 days.

MV has to copy the subdirectory structure of the source-path if necessary. MV only move files older than 90 days.

Things like this won't work. It does place all the files in the same destination folder without taking the source subdirectory-structure in to account.

find -type f -mtime +600 -exec mv '{}' /storage/9016-4EF8/WhatsApp/Media/ \;

2 Answers 2


With zsh, you can do something like:

cd /path/sourcefolder &&
  for f (**/*(ND.m+89)) {
    mkdir -p -- $dest/$f:h &&
      mv -- $f $dest/$f

You can make it quicker by issuing a zmodload zsh/files beforehand which will replace mv and mkdir with builtin versions.


cd /path/sourcefolder &&
  dest=/storage/9016-4EF8/WhatsApp/Media find . -type f -mtime +89 -exec sh -c '
     for f do
       mkdir -p -- "$dest/${f%/*}" &&
         mv -- "$f" "$dest/$f"
     done' sh {} +

(note: -mtime -89 selects files that are less than 89 days old, -mtime 89 files that are 89 to 90 days old and -mtime +89 files that are 90 days old or older (or at least were at the time find was started and note that here day refers to 24-hour periods regardless of whether there's been DST clock changes in the interval)).

In any case, note that while mv will try to preserve most of the attributes of the original files when copying them to the external media, the directory themselves will be created there as new with default ownership, permissions and attributes (unlikely to be an issue in your case as the destination folder doesn't sound it is even on a Unix-like file system).


The script is going to work for directories/files names with spaces. How it is here, it prints commands instead of executing them. If you want to actually execute commands, remove the "echo" word from the code (in two places).

Oneliner to be executed using shell (/bin/sh or /bin/bash):

DAYS=89; SOURCE="/path/sourcefolder/"; DEST="/path/destinationfolder/"; find "${SOURCE}" -mtime +${DAYS}|while read -r S; do D=$(echo "${S}"|sed "s#^${SOURCE}#${DEST}#g"); DD=$(dirname "${D}"); [ -d "${DD}" ] || echo mkdir -p \"${DD}\"; echo mv -f \"${S}\" \"${D}\"; done


# define number of days
# source directory
# destination directory
# take one by one all files from source directory
find "${SOURCE}" -mtime +${DAYS}|while read -r S; do
   # create destination file path, based on source file path
   D=$(echo "${S}"|sed "s#^${SOURCE}#${DEST}#g");
   # find directory, where distination file should be placed
   DD=$(dirname "${D}");
   # if destination directory does not exist, create it
   [ -d "${DD}" ] || echo mkdir -p \"${DD}\";
   # move source file to destination
   echo mv -f \"${S}\" \"${D}\";
  • 1
    It won't work with file names with newline characters or filenames that end in spaces (the command to read a line is IFS= read -r S, not just read -r S). You'll have problems with file names containing regex operators or # as well. There are some spurious backslashes before some of you "s as well which means those are passed literally to the application and the parameter expansion within ends up not being quoted. Jan 6, 2020 at 20:50
  • @Stéphane Chazelas : I agree with specified downsides; my goal was to provide with an easier to understand and customize variant
    – Yurko
    Jan 6, 2020 at 20:56

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.