Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have created the following script that move old days files as defined from source directory to destination directory. It is working perfectly.


echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days

 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

This script moves files great, It also move files of source subdirectory, but it doesn't create subdirectory into destination directory. I want to implement this additional feature in it.

with example

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

When I run this script , it also move hex's files in maxi directory, but I need that same hex should be created into maxi directory and move its files in same hex there.

share|improve this question
up vote 4 down vote accepted

Instead of running mv /home/ketan/hex/foo /home/maxi, you'll need to vary the target directory based on the path produced by find. This is easier if you change to the source directory first and run find .. Now you can merely prepend the destination directory to each item produced by find. You'll need to run a shell in the find … -exec command to perform the concatenation, and to create the target directory if necessary.

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

Note that to avoid quoting issues if $destination contains special characters, you can't just substitute it inside the shell script. You can export it to the environment so that it reaches the inner shell, or you can pass it as an argument (that's what I did). You might save a bit of execution time by grouping sh calls:

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
' "$destination" {} +

Alternatively, in zsh, you can use the zmv function, and the . and m glob qualifiers to only match regular files in the right date range. You'll need to pass an alternate mv function that first creates the target directory if necessary.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'
share|improve this answer
for x do, you've got a missing ; there :). Also, I have no idea what you wanted to achieve with $0 but I'm quite convinced it would be sh :). – Michał Górny Oct 12 '14 at 19:02
@MichałGórny for x; do is technically not POSIX-compliant (check the grammar), but modern shells allow both for x do and for x; do; some old Bourne shells didn't grok for x; do. On modern shells, with sh -c '…' arg0 arg1 arg2 arg3, arg0 becomes $0, arg1 becomes $1, etc. If you want $0 to be sh, you need to write sh -c '…' sh arg1 arg2 arg3. Again, some Bourne shells behaved differently, but POSIX specifies this. – Gilles Oct 12 '14 at 19:11
Thanks for the explanation. Always learning :). – Michał Górny Oct 12 '14 at 19:46

It's not as efficient, but the code is easier to read and understand, in my opinion, if you just copy the files and then delete after.

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;
share|improve this answer

you could do it using two instances of find(1)

There's always cpio(1)

(cd "$soure" && find … | cpio -pdVmu "$destination")

Check the arguments for cpio. The ones I gave

share|improve this answer
This will break on any filenames with whitespace. – Chris Down Dec 21 '12 at 17:26
This copies files instead of moving them. – Gilles Dec 22 '12 at 0:01
May not be a perfect answer, but it helped me to move files preserving paths in a more or less straight forward manner (I have enough space to copy the files then delete). Upvoted – AhHatem Apr 27 '14 at 10:01

I know find was specified, but this sounds like a job for rsync.

I most often use the following:

rsync -axuv --delete-after --progress Source/ Target/

Here is a good example if you want to only move files of a particular file-type (From here):

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/
share|improve this answer

You can do this by appending the absolute path of the file returned by find to your destination path:

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'
share|improve this answer
Chris now its moves whole home into maxi – K.K Patel Dec 21 '12 at 15:11
@K.KPatel No, it doesn't. It merely keeps your directory structure. – Chris Down Dec 21 '12 at 17:26
This breaks if $destination contains special characters, because it undergoes expansion in the inner shell. Maybe you meant destination='\'"$destination"\''? That still breaks on '. Also, this creates files such as /home/maxi/home/ketan/hex/foo instead of /home/maxi/hex/foo. – Gilles Dec 22 '12 at 0:03

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.