38

How can I list recursively all files that were changed between 22.12.2011 and 24.12.2011?

  • 6
    If you mean 'last changed', you have a chance at a solution. If a file was modified on 26.12.2011, you cannot tell if it was also modified during your given range. (Unless you have a very exotic file system.) – William Pursell Jan 17 '12 at 2:07
25

Generally speaking, when you're looking for files in a directory and its subdirectories recursively, use find.

The easiest way to specify a date range with find is to create files at the boundaries of the range and use the -newer predicate.

touch -t 201112220000 start
touch -t 201112240000 stop
find . -newer start \! -newer stop
  • why negating the output of the first -newer is needed here? – Alexander Cska Dec 16 '18 at 15:42
  • @AlexanderCska The first -newer isn't negated, the second one is. “Find files that are newer than start but not newer than stop”. – Gilles 'SO- stop being evil' Dec 16 '18 at 15:52
  • Ok negation works on the command after. What would happen if no negation was present? – Alexander Cska Dec 16 '18 at 17:14
  • @AlexanderCska Then you'd get the files that are newer than both start and stop. – Gilles 'SO- stop being evil' Dec 16 '18 at 18:24
35

Using Gilles' solution and after reading the man find(1) again I found a more simple solution. The best option is the -newerXY. The m and t flags can be used.

m   The modification time of the file reference
t   reference is interpreted directly as a time

So the solution is

find . -type f -newermt 20111222 \! -newermt 20111225

The lower bound in inclusive, and upper bound is exclusive, so I added 1 day to it! And it is recursive. It works well on find v4.5.9.

  • Cool trick, thanks! That helped me with finding a recent file containing a certain keyword in a directory with thousands of files: find -newermt 20150212 \! -newermt 20150213 | xargs grep 'keyword' -m 50 -l (-m 50 = search in first 50 lines). – Rob W Feb 13 '15 at 10:20
  • @RobW: Actually you can spare the pipe and xargs using -exec ... + in find(1), like: find -newermt 20150212 \! -newermt 20150213 -exec grep keyword -m 50 -l {} +. This does the same, but cheaper. – TrueY Feb 13 '15 at 10:27
  • I had to enclose the timestamps with double quote (I'm on Ubuntu Xenial with find 4.7.0-git). – IsaacS Dec 27 '16 at 21:13
13

In addition to the answers already given, note that you can directly specify your dates:

find -type f -newermt "2011-12-22" \! -newermt "2011-12-24"

or

find -type f -newermt "2011-12-22 00:00:00" \! -newermt "2011-12-24 13:23:00"

if you additionally want to specify the time.

6

Assuming you don't need precision to the seconds, this should work.

find . -type f -mmin -$(((`date +%s`-`date -d 20111222 +"%s"`)/60)) \! -mmin +$(((`date +%s`-`date -d 20111224 +"%s"`)/60))

EDIT: Changed cmin to mmin after @Eelvex's comment. EDIT: '\!' missing

  • hm doesnt seem to work. are you sure it recursively walks through the subdirectories? – clamp Jan 16 '12 at 22:29
  • Yes, it works on me. Do you really have files modified in that time range? Try it with different time ranges. – onur güngör Jan 16 '12 at 22:32
  • 6
    -cmin is "status change", -mmin is "data change". You probably want -mmin – Eelvex Jan 17 '12 at 0:01
3

find can take an ISO formatted datetime, so for a server on UTC for instance, you could specify an offset of a number of hours from wherever you are. This also takes care of having to add a day since you're comparing time too:

find -type f -newermt 20111224T0800 \! -newermt 20111225T0800
0

Please note that the question marked as duplicate, and therefore is restricted from answering it directly, is why I'm posting into here. This answer is for answering the question "need to move files to different folder based on the creation date [duplicate]"

The answers are reasonable but there were some limitations with a purely find command. I wrote this shell script to go through a directory with so many files in it the filesystem was gagging on the metadata trying to perform an ls. Additionally some *nix systems will barf a too many arguments error with running ls.

The find command is very powerful and I started with the methods that are listed but I had so many years of data throughout that directory that I had to pass over all the files repeatedly. This produces a lot of needless passed over each file. I tried doing one screen per year and running multiple finds but this resulted in a lot of errors from each find and files would go missing when one of the finds moved it.

My shell script moves a file to the destination directory, with a 4 digit year and a 2 digit month. It can easily be expanded to a 2 digit day by uncommenting a couple lines and commenting out their counterparts. I believe it's more efficient because it was perform the moves in one pass of find so multiple find commands and passes over the directory are unneeded.

#!/bin/bash
#We want to exit if there is no argument submitted
if [ -z "$1" ]
then
  echo no input file
  exit
fi

#destDir should be set to where ever you want the files to go
destDir="/home/user/destination"

#Get the month and year of modification time
#--format %y returns the modification date in the format:
# 2016-04-26 12:40:48.000000000 -0400
#We then take the first column, split by a white space with awk
date=`stat "$1" --format %y | awk '{ print $1 }'`

#This sets the year variable to the first column split on a - with awk
year=`echo $date | awk -F\- '{print $1 }'`
#This sets the month variable to the second column split on a - with awk
month=`echo $date | awk -F\- '{print $2 }'`
#This sets the day variable to the third column split on a - with awk
#This is commented out because I didn't want to add day to mine
#day=`echo $date | awk -F\- '{print $3 }'`

#Here we check if the destination directory with year and month exist
#If not then we want to create it with -p so the parent is created if
# it doesn't already exist
if [ ! -d $destDir/$year/$month ]
then
  mkdir -p $destDir/$year/$month || exit
fi

#This is the same as above but utilizes the day subdirectory
#Uncommented this out and comment out the similar code above
#if [ ! -d $destDir/$year/$month/$day ]
#then
#  mkdir -p $destDir/$year/$month$day || exit
#fi

#Echoing out what we're doing
#The uncommented is for just year/month and the commented line includes day
#Comment the first and uncomment the second if you need day
echo Moving $1 to $destDir/$year/$month
#echo Moving $1 to $destDir/$year/$month/$day

#Move the file to the newly created directory
#The uncommented is for just year/month and the commented line includes day
#Comment the first and uncomment the second if you need day
mv "$1" $destDir/$year/$month
#mv "$1" $destDir/$year/$month/$day

Once saved and made executable you may call this script with find like the following.

find /path/to/directory -type f -exec /home/username/move_files.sh {} \;

You don't need to worry about setting the newermt option for find as all find is providing is a single execution per file found and the script makes all the decisions.

Keep in mind that if you don't select -type f it will move directories around and that will likely cause issues. You could also use -type d if you're looking to move just directories. Not setting the type will almost certainly produce unwanted behaviour.

Remember that this script was tailoured to my needs. You may want to simply use my construct as inspiration for a better suited for your needs script. Thank you!

Considerations: The efficiency of the command can be GREATLY improved by allowing an unlimited number of arguments to pass through the script. This is actually relatively easy if you pass over the $@ variable. Extending the functionality of this would allow us to use find's -exec + function or leverage xargs, for example. I may quickly implement that and improve my own answer but this is a start.

Since this was a one-shot script session there are probably many improvements that can be made. Good luck!

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.