Recursively find all files newer than a given time
This is a bit circuitous because touch
doesn't take a raw time_t
value, but it should do the job pretty safely in a script. (The -r
option to date
is present in MacOS X; I've not double-checked GNU.) The 'time' variable could be avoided by writing the command substitution directly in the touch
command line.
time=$(date -r 1312603983 '+%Y%m%d%H%M.%S')
marker=/tmp/marker.$$
trap "rm -f $marker; exit 1" 0 1 2 3 13 15
touch -t $time $marker
find . -type f -newer $marker
rm -f $marker
trap 0
find files modified within given time range
-newermt
primary doesn't accept a time range in any format. To select files modified within let's say the period A-B
, you need two -newermt
s; one to include files newer than A
, the other to exclude files newer than B
.
Further, there are two edge cases that need to be dealt with:
- The user might enter 08 or 09 as both are valid hours. But as both have a leading zero, Bash would treat them as octal numbers in an arithmetic context, and raise an error since 8 and 9 are not valid digits in base 8.
- When the user entered 0, to include files modified at 00:00 too, inclusive
-newermt
's argument has to be yesterday's 23:59:59.
So, I would do it like this instead:
#!/bin/bash -
LC_COLLATE=C
read -rp 'hour ([0]0-23): ' hour
case $hour in
(0|00)
find /home/mikepnrs \
-newermt 'yesterday 23:59:59' \
! -newermt '00:59:59' ;;
(0[1-9]|1[0-9]|2[0-3])
find /home/mikepnrs \
-newermt "$((10#$hour-1)):59:59" \
! -newermt "$hour:59:59" ;;
(*)
printf 'invalid hour: %q\n' "$hour" >&2
exit 1
esac
Find out whether a file newer than a given date/time exists in a directory?
Your problem can be translated into multiple easy subproblems
Q: How do I recursively look at each file inside a directory?
A:
use File::Find
. This would look a bit likeuse File::Find;
find sub {
return unless -f;
if (file_is_newer_than($timestamp)) {
do something;
},
}, $top_dir;Q: How do I do this for multiple directories?
A: Wrap it in a foreach loop, e.g.
for my $dir_time (["./foo", 1234567890], ["./bar", 1230987654]) {
my ($top_dir, $timestamp) = @$dir_time;
# above code
}Q: How do I determine if the file is newer?
A:
stat
it formtime
orctime
, then compare result with your timestamp. E.g.use File::stat;
say "$_ is new!" if stat($_)->mtime > $timestamp;Q: I'm only interested whether or not any such files exist at all. How can I short curcuit the
find
?A: Tricky one. We can't just
return
from thefind
, because that would just exit from the coderef we passed it. Instead, we can use the exceptions-for-control-flow antipattern:eval {
find {
wanted => sub {
return unless -f;
die "New file found\n" if stat($_)->mtime > $timestamp;
},
no_chdir => 1,
} $top_dir;
};
if ($@) {
# I should really use exception objects here…
if ($@ eq "New file found\n") {
say "New file in $top_dir found";
} else {
die $@; # rethrow error
}
}I set the
no_chdir
option so that I don't have to restore the correct working directory in the exception handler.Or we could use loop control on labeled blocks:
DIR: for my $dir_time (...) {
my ($top_dir, $timestamp) = @$dir_time;
RECURSION: {
find {
wanted => sub {
return unless -f;
last RECURSION if stat($_)->mtime > $timestamp; # exit the RECURSION block
},
no_chdir => 1,
} $top_dir;
# if we are here, no newer file was found.
next DIR; # make sure to skip over below code; go to next iteration
}
# this code only reached when a newer file was found
say "New file found";
}While this doesn't abuse exceptions for control flow, this will trigger warnings:
Exiting subroutine via last
We can silence this with
no warnings 'exiting'
.
NB: All code in here is quite untested.
How can I recursively find all files in current and subfolders based on wildcard matching?
Use find
:
find . -name "foo*"
find
needs a starting point, so the .
(dot) points to the current directory.
How to recursively find the latest modified file in a directory?
find . -type f -printf '%T@ %p\n' \
| sort -n | tail -1 | cut -f2- -d" "
For a huge tree, it might be hard for sort
to keep everything in memory.
%T@
gives you the modification time like a unix timestamp, sort -n
sorts numerically, tail -1
takes the last line (highest timestamp), cut -f2 -d" "
cuts away the first field (the timestamp) from the output.
Edit: Just as -printf
is probably GNU-only, ajreals usage of stat -c
is too. Although it is possible to do the same on BSD, the options for formatting is different (-f "%m %N"
it would seem)
And I missed the part of plural; if you want more then the latest file, just bump up the tail argument.
Shell Script — Get all files modified after date
as simple as:
find . -mtime -1 | xargs tar --no-recursion -czf myfile.tgz
where find . -mtime -1
will select all the files in (recursively) current directory modified day before. you can use fractions, for example:
find . -mtime -1.5 | xargs tar --no-recursion -czf myfile.tgz
Related Topics
How to Tar Certain File Types in All Subdirectories
How to Find Files Recursively by File Type and Copy Them to a Directory
How to Convert an Image to Grayscale via the Command Line
Redirecting Tcp-Traffic to a Unix Domain Socket Under Linux
Does the Thundering Herd Problem Exist on Linux Anymore
How to Statically-Link a Complex Program
How Does Fork() Return for Child Process
How to Delete First Two Lines and Last Four Lines from a Text File with Bash
Macros for Gcc/G++ to Differentiate Linux and MAC Osx
Differencebetween Xterm-Color & Xterm-256Color
What Does Anon-Rss and Total-Vm Mean
Permanently Change Disassembly Flavor in Gdb
Execute a Shell Script Everyday at Specific Time
How Is It Possible That Kill -9 for a Process on Linux Has No Effect
String Comparison Not Working Properly