22

I need to move files based on a year. I used the find command

find /media/WD/backup/osool/olddata/ -mtime +470 -exec ls -lrth {} \;|sort -k6

but for this command to successfully execute i need to know the exact mtime now 470 is just a guess. Means if I can give the year 2012 it gives me files only related to 2012.

So i need advice on how to

Find files based on year e.g 2012 and move them to other directory.

OS release 5.2

FIND version
GNU find version 4.2.27
Features enabled: D_TYPE O_NOFOLLOW(enabled) LEAF_OPTIMISATION SELINUX 
2
  • Does it make sense to use -exec ls ... \; instead of -exec ... +? Is your find too old for that? In that case find ... -print0 | xargs -0 ls... would be much faster. Apr 22, 2013 at 13:27
  • 1
    I wish these questions (and answers) that deal with coreutils would routinely specify whether we're dealing with GNU or BSD (or other) utils.
    – labyrinth
    Jan 23, 2014 at 17:44

5 Answers 5

54

You want to use the -newermt option for find:

find /media/WD/backup/osool/olddata/ -newermt 20120101 -not -newermt 20130101

to get all the files with modification time in 2012.

If your find does not support -newermt you can also do the following to prevent using offset calculations:

touch -d 20120101 /var/tmp/2012.ref
touch -d 20130101 /var/tmp/2013.ref
find /media/WD/backup/osool/olddata/ -newer /var/tmp/2012.ref -not -newer /var/tmp/2013.ref

Manpage

-newerXY reference
          Compares the timestamp of the current file with reference.   The
          reference  argument  is  normally the name of a file (and one of
          its timestamps is used for the comparison) but it may also be  a
          string  describing  an  absolute time.  X and Y are placeholders
          for other letters, and these letters select which time belonging
          to how reference is used for the comparison.

          ...

          m   The modification time of the file reference
          t   reference is interpreted directly as a time
8
  • 1
    -newermt flag is not available in Find command
    – OmiPenguin
    Apr 22, 2013 at 12:56
  • That must be an old version of find that your RHEL comes with? I will provide an alternative in the answer for that
    – Anthon
    Apr 22, 2013 at 13:03
  • Yes this one is 5.2
    – OmiPenguin
    Apr 22, 2013 at 13:05
  • 3
    The newermt switch didn't show up in find until versions after v4.3.3, gnu.org/software/findutils/manual/html_mono/find.html.
    – slm
    Apr 22, 2013 at 13:41
  • 1
    You can workaround this : use touch to create a /tmp/ANCHORJAN2012 file of date 2012/01/01 0h00 ...... , and same for /tmp/ANCHORJA2013file (Jan 1 2013 0:00). Then: find /path \( -newer ANCHORJAN2012 -a \! -newer ANCHORJAN2013 \) -print Apr 22, 2013 at 14:16
6
touch --date=2011-12-31T23:59:59 start
touch --date=2012-12-31T23:59:59 stop
find / -newer start \! -newer stop -printf %Tx" "%p\\n

-exec ls doesn't make any sense.

3
  • 1
    -exec ls -l makes more sense, and even more while testing.
    – user
    Apr 22, 2013 at 13:17
  • @MichaelKjörling find can print everything that ls can. OK, with one exception: escaping / quoting of unpleasant chars. But in general the output of find can be sorted directly. Useless calls of ls don't make the whole thing faster. Apr 22, 2013 at 13:28
  • I very often use something quite similar (although personally I prefer -print or possibly -ls) while testing. The advantage of using -exec ls {} is that it already executes something external to find. So by the time you switch from ls to mv, the actual changes are minimal.
    – user
    Apr 22, 2013 at 13:31
5

According to the man page, the argument to -mtime is the number of days you're looking for. You can use date +%j to find the number of days since January 1st of the current year.

3
  • 2
    Nice way. To be very "nitpicky" : -mtime -1 : is any file in the last 24hours. Not just the files from yesterday (or today). So, there will also be a little "overlap" : if you determine it's been 120 days since 2012, and today it is 15:45 when you launch the script, you'll end up selecting files up to dec 31 15:45 (and not the last ones of that day)... You can probably use: nb_day - 1, and then "weed out" the few ones who dates from 2013 (those could be found by a ls -l | grep -v, BUT the output differ if they are 1 day old only... lots of pitfalls...) Apr 22, 2013 at 12:53
  • You do want to use the -daystart option if you are going to work with -mtime. Otherwise you will have problems with files around the beginning and end of the year whatever way you turn unless you run this exactly at midnight. You can do the offset more accurately with -mmin than with -mtime if you can find the number of minutes since the new year easily. But the same problem exists with that as with -mtime, just for fewer files (those one minute around midnight newyear).
    – Anthon
    Apr 22, 2013 at 13:49
  • 1
    Yeah, re-reading the man page I am seeing how it breaks it down to hours so doing a simple find -mtime $("date -+%j") probably won't give the OP what they're going for. I'm leaving it up for posterity though since this seems like a possible blindspot for people to fall into.
    – Bratchley
    Apr 22, 2013 at 15:03
2

You can try to use a combination with the stat and/or date commands as in the following one-liners:

BSD / Mac OS X:

find . -type f -print | while read file; do eval `stat -s "$file"`; year=`date -jf "%s" "+%Y" "$st_mtime"`; dest="/path/to/destination/$year; mkdir -p "$dest"; mv "$file" "$dest"; done

Linux:

find . -type f -print | while read file; do year=`date -r "$file" "+%Y"`; dest="/path/to/destination/$year; mkdir -p "$dest"; mv "$file" "$dest"; done

In both cases the approach is to use the date command to extract the year from the last modification time of every file and then to construct a path with this year. The command mkdir -p will create this path if it doesn't exist already. Of course you can use some other find options to refine your search.

1

If you do this from the working directory, you can do this instead:

ls -l |awk '{ if ($8 == "2013") print $9 }'

This simplifies things considerably and doesn't cause any overlap. But it also assumes that these files are older than 6 months old, and that ls will print the year instead of the exact time.

For files newer than 6 months old, you'll just have to replace it with this:

ls -l |awk '{ if ($6 == "May") print $9 }' 

or something similar, depending on the month. If you want to create a list of months to move files for (or if you want to create a list for years) do this:

month="May Jun Jul"; 
for i in `echo $month`; 
do 
    for j in `ls -l |awk '{ if ($6 == "'$i'") print $9}'`
    do
        mkdir -p $i
        mv $j $i
    done
done

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .