I have a folder with more than 30 sub directories and I want to get the list of the files which was modified after a specified date(say sep 8 which is the real case) and to be copied with the same tree structure with only the modified files in that folder
I have say 30 dir from that I have the list of the files I need found using last modified date
Find command output
a/a.txt a/b/b.txt a/www.txt etc..
For example I want the folder “a” created and only the a.txt in it…like wise for the other also “a/b” to be created and b.txt inside it…
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Assuming you have your desired files in a text file, you could do something like
while IFS= read -r file; do
echo mkdir -p ${file%/*};
cp /source/"$file" /target/${file%/*}/${file##*/};
done < files.txt
That will read each line of your list, extract the directory and the file name, create the directory and copy the file. You will need to change source and target to the actual parent directories you are using. For example, to copy /foo/a/a.txt to /bar/a/a.txt, change source to foo and target to bar.
I can’t tell from your question whether you want to copy all directories and then only specific files or if you just want the directories that will contain files. The solution above will only create the necessary directories. If you want to create all of them, use
find /source -type d -exec mkdir -p {} /target
That will create the directories. Once those are there, just copy the files:
while IFS= read -r file; do
cp /source/"$file" /target/"$file"
done
Update
This little script will move all the files modified after September 8. It assumes the GNU versions of find and touch. Assuming you’re using Linux, that’s what you will have.
#!/usr/bin/env bash
## Create a file to compare against.
tmp=$(mktemp)
touch -d "September 8" "$tmp"
## Define the source and target parent directories
source=/path/to/source
target=/path/to/target
## move to the source directory
cd "$source"
## Find the files that were modified more recently than $tmp and copy them
find ./ -type f -newer "$tmp" -printf "%h %p" |
while IFS= read -rd '' path file; do
mkdir -p "$target"/"$path"
cp "$file" "$target"/"$path"
done
Strictly speaking, you don’t need the tmp file. However, this way, the same script will work tomorrow. Otherwise, if you use find’s -mtime, you would have to calculate the right date every day.
Another approach would be to first find the directories, create them in the target and then copy the files:
-
Create all directories
find ./ -type d -exec mkdir -p ../bar/{} ; -
Find and copy the relevant files
find ./ -type f -newer "$tmp" -exec cp {} /path/to/target/bar/{} ; -
Remove any empty directories
find ../bar/ -type d -exec rmdir {} ;
Method 2
pax
Pax is the best tool for this task. It’s POSIX’s replacement for cpio and tar (and unlike tar it includes a pass-through mode, not just archive creation and extraction). Sadly, it’s omitted from the default installation in some Linux distributions, but it’s only an apt-get/yum/emerge/… invocation away.
Pax has a limitation: recently modified directories will be copied even if they contain no file to be copied. You can remove empty directory afterwards (unless there are pre-existing empty directories in the destination that you want to preserve).
cd /path/to/source
pax -rw -pp -T 201409080000 . /path/to/destination/
find /path/to/destination/ -depth -type d -exec rmdir {} +
I’ll mention other ways that can be adapted to similar but not identical use cases where pax doesn’t have a filter.
Zsh
Use the glob qualifier m to match files by their modification time (e.g. m-10 for files modified in the past 10 days; you can use other units instead), and . to match regular files. The history modifier h retains the directory part of the file name.
cd /path/to/source for x in **/*(.m-10); do mkdir -p -- $x:h cp -p -- $x /path/to/destination/$x done
Alternatively, you can use the zmv function to copy files matched by a wildcard expression. There’s no built-in way to create the destination directory, so I provide a function to do it.
autoload -U zmv
mkdir_cp () {
mkdir -p -- ${(P)#}:h
cp -- <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="624622">[email protected]</a>
}
cd /path/to/source
zmv -p mkdir_cp -Q '**/*(.m-10)' '/path/to/destination/$f'
POSIX find
With find, you can use -mtime -10 to match files modified in the past 10 days, or -newer reference_time to match files modified after reference_files.
touch -t 201409080000 /tmp/reference_time
cd /path/to/source
find . -type f -newer /tmp/reference_time -exec sh -c '
mkdir -p /path/to/destination/"${0%/*}"
cp "$0" "/path/to/destination/$0"
' {} ;
Method 3
You could use find with rsync to do this.
find ./source/ -newer /tmp/foo -print0 | rsync -av --files-from=- --from0 ./ ./destination/
Testing
- I created 2 directories named source and destination.
-
For testing purposes, I created a temporary file with a timestamp as
below.touch --date "2014-09-08" /tmp/foo
-
Now, inside source directory, I created bunch of sub-directories
that need to be copied to the destination directory (some sub-directories with spaces as well, for testing purposes). I also created some sample files. The
destination directory is currently empty. -
I have changed the creation date for one of the files inside the sub
directory.touch -c -t 1110111730 oldfile
-
Now, I execute the
findcommand as I had specified. Thefind
command finds all the files based on the condition that we specify
and thersyncafter the pipe gets the list of files that our
findcommand has produced.
Now, inside the destination directory, the file named oldfile shouldn’t be there as it has a time previous than the one that we desire.
So, I go inside the destination directory to check and I have all the sub directory structure as I desire and all the files that matched our find condition and the file named oldfile is not present.
References
http://www.commandlinefu.com/commands/view/1481/rsync-find
Method 4
This is exactly how cpio was designed to be used.
find . -whatever -print | cpio -pvmd /path/to/copy/to/.
Done.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0