问题
I have the following files
./path/to/stuff1/file1 (x)
./path/to/stuff1/file2
./path/to/stuff1/file3
./path/to/stuff2/file1
./path/to/stuff2/file2 (x)
./path/to/stuff2/file3
./path/to/stuff3/file1 (x)
./path/to/stuff3/file2
./path/to/stuff3/file3
where I marked the files I touched lastly. I want to get exactly those marked files. In other words:
- I want to get the up-to-date file for each directory.
I constructed the bash command
for line in $( find . -name 'file*' -type f | awk -F/ 'sub($NF,x)' | sort | uniq ); do
find $line -name 'file*' -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2 -d' '
done
which I am able to use in perl using the system command and escaping the $. Is it possible to do this directly in perl or do you think my approach is fine?
edit
If possible the task should be done in perl without using external modules.
edit2
Sorry, I noticed my question wasn't clear. I thought the answer of @TLP would work but I have to clearify: I want to check for the newest file in each folder, e.g. the newest file in stuff1. Say I do
touch ./path/to/stuff1/file1
touch ./path/to/stuff2/file2
touch ./path/to/stuff3/file1
before I run the script. It then should output:
./path/to/stuff1/file1
./path/to/stuff2/file2
./path/to/stuff3/file1
The filename can be identical for different stuff but only one file per path should be output.
The script of @codnodder does this but I wish to search for only for the filename and not for the full path. So I want to search for all files beginning with file and the script should search recursively.
回答1:
Your find command can be emulated with File::Find's find command. This is a core module in Perl 5, and is almost certainly already on your system. To check the file modification time, you can use the -M file test.
So something like this:
use strict;
use warnings;
use File::Find;
my %times;
find(\&wanted, '.');
for my $dir (keys %times) {
print $times{$dir}{file}, "\n";
}
sub wanted {
return unless (-f && /^file/);
my $mod = -M $_;
if (!defined($times{$File::Find::dir}) or
$mod < $times{$File::Find::dir}{mod}) {
$times{$File::Find::dir}{mod} = $mod;
$times{$File::Find::dir}{file} = $File::Find::name;
}
}
If I run this command in my test directory, on my system, I get the following Data::Dumper structure, where you can clearly see the file name key, the full path stored in the file key, and the modification date (in days compared to the run time of the script) as the mod.
$VAR1 = {
'./phone' => {
'file' => './phone/file.txt',
'mod' => '3.47222222222222e-005'
},
'./foo' => {
'file' => './foo/fileb.txt',
'mod' => '0.185'
},
'.' => {
'file' => './file.conf',
'mod' => '0.154490740740741'
}
};
回答2:
There are 3 general approaches I can think of.
- Using opendir(), readdir(), and stat().
- using File::Find.
- Using glob().
The most appropriate option depends on the specifics of what you have to work with, that we can't see from your posting.
Also, I assume when you say "no external modules", you are not excluding modules installed with Perl (i.e., in Core).
Here is an example using glob():
use File::Basename qw/fileparse/;
for my $file (newest_file()) {
print "$file\n";
}
sub newest_file {
my %files;
for my $file (glob('./path/stuff*/file*')) {
my ($name, $path, $suffix) = fileparse($file);
my $mtime = (stat($file))[9];
if (!exists $files{$path} || $mtime > $files{$path}[0]) {
$files{$path} = [$mtime, $name];
}
}
return map { $files{$_}[1] } keys %files;
}
来源:https://stackoverflow.com/questions/20540967/find-up-to-date-files-for-different-paths-but-with-identical-file-names