1

I'm attempting to find and copy files that have been modified within the last day. While doing that I'm trying maintain folder structures under dir1. There are many subdirectories under dir1 and data files within those.

Below is what I cobbled together so far but apparently mtime is not updated on directories if the contents of a file is updated. Also I believe this cp command will copy all of the files in a directory rather than just those that have changed.

find /NAS/data/dir1 -type d -mtime -1 -exec cp -at /mnt/databackup/dir1 {} +

If I used -type f it would not retain the folder structure.

  • Why "within the last day"? Is this a script you will run daily? So if you run it Monday, it should catch files changed within the last 24 hours, but ignore files that were changed say, on Saturday? Or do you really want a script that finds and copies all files that have been changed since the last time the script was run? Putting that another way, what if you forget to run the script one day? Should the next run of the script copy the files that got missed because of the day you forgot to run it? Is rsync the tool you're really looking for? – Jim L. Apr 29 at 20:44
  • 1
    I would also guess rsync is the simpler and more robust solution here. And I wouldn't rely on mtime (and file size) and use option --checksum even if it takes longer. – Freddy Apr 29 at 20:54
0

You could use find ... -type f, with a bash for-loop. In the loop, a combination of mkdir -p and dirname would let you recreate the folder structure, before simply copying each file with cp.

#!/bin/bash

dirA="/NAS/data/dir1/"
dirB="/mnt/databackup/dir1/"
while read -r file
do
    relative="${file##"$dirA"}"
    newfile="$dirB$relative"
    echo mkdir -p "$(dirname "$file")"
    echo cp -at "$file" "$newfile"
done < <(find "$dirA" -type f -mtime -1)
  • read is a bash builtin. It is classically used with -r to avoid some edge-case problems. It reads one line (at a time) from it's stdin. Here, that stdin is the output of find.
  • ${var##string-to-remove} is a form of parameter substitution
  • newfile="$dirB$relative". This is a classical bash concatenation
  • $(). This bash construct is called command substitution
  • mkdir -p create a directory and all it's ancestors as needed
  • cmd < file this is a classic redirection
  • <(cmd) this is called process substitution. It substitute a filename as handle to the stdin [>()] or stdout [<()] of a command.

You can use explainshell.com to get more detailed explanations.

  • It would be safer to execute the loop from within find with e.g. find ... -exec sh -c 'for file do ...; done' sh {} +. – Kusalananda Apr 29 at 21:08

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.