[英]Ignore an entire directory when using File::Find in Perl script
我有一個腳本,可以掃描每個本地文件系統中可寫世界的文件。 找到的所有文件都將寫入輸出文件。 它還使用另一個文件,該文件提供要忽略的文件列表。
我們已經安裝了Tivoli監視代理程序,出於某種奇怪的原因,該代理程序被設計為在其安裝路徑中使用世界可寫權限創建每個文件。 眾所周知,我們對此幾乎無能為力,我們只想忽略整個目錄。
我想我可以利用諸如/opt/IBM/ITM/*
但是我對如何做到這一點尚無頭緒。
目前,我已將目錄硬編碼到腳本中。 這不理想,但是可以起作用。 我希望將其包含在排除列表中。
在Code Review的上方 ,建議我使用File::Find::prune
。 不幸的是,這沒有用。 根據我對File::Find::prune
了解,如果它在/opt/IBM/ITM/.../.../file.txt
找到了應該排除的文件,則它將跳過整個/opt/IBM/ITM/.../.../
目錄。 很好,但這意味着我需要為/opt/IBM/ITM/
每個子目錄都有一個排除項。 考慮到有多少個子目錄和子目錄,這將是一個繁瑣的工作。
我確實嘗試過將世界可寫文件放置在/opt/IBM/ITM/
,並將其添加到排除列表中,但這沒有用。 我猜是因為沒有找到它。
劇本:
#!/usr/bin/perl
use warnings;
use strict;
use Fcntl ':mode';
use File::Find;
no warnings 'File::Find';
no warnings 'uninitialized';
my $dir = "/var/log/tivoli/";
my $mtab = "/etc/mtab";
my $permFile = "world_writable_files.txt";
my $tmpFile = "world_writable_files.tmp";
my $exclude = "/usr/local/etc/world_writable_excludes.txt";
my $mask = S_IWUSR | S_IWGRP | S_IWOTH;
my (%excludes, %devNums);
my $errHeader;
# Compile a list of mountpoints that need to be scanned
my @mounts;
open MT, "<${mtab}" or die "Cannot open ${mtab}, $!";
# We only want the local mountpoints
while (<MT>) {
if ($_ =~ /ext[34]/) {
chomp;
my @line = split;
push(@mounts, $line[1]);
my @stats = stat($line[1]);
$devNums{$stats[0]} = undef;
}
}
close MT;
# Build a hash from /usr/local/etc/world_writables_excludes.txt
if ((! -e $exclude) || (-z $exclude)) {
$errHeader = <<HEADER;
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! /usr/local/etc/world_writable_excludes.txt is !!
!! is missing or empty. This report includes !!
!! every world-writable file including those which !!
!! are expected and should be excluded. !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
HEADER
} else {
open XCLD, "<${exclude}" or die "Cannot open ${exclude}, $!\n";
while (<XCLD>) {
chomp;
$excludes{$_} = 1;
}
}
sub wanted {
my @dirStats = stat($File::Find::name);
# Is it excluded from the report...
return if exists $excludes{$File::Find::name};
# ...is the Tivoli installation directory...
return if ($File::Find::name =~ /\b\/ITM\b/);
# ...in a special directory, ...
return if ($File::Find::name =~ /^\bsys\b|\bproc\b|\bdev\b$/);
# ...a regular file, ...
return unless -f;
# ...local, ...
return unless (exists $devNums{$dirStats[0]});
# ...and world writable?
return unless ($dirStats[2] & $mask) == $mask;
# If so, add the file to the list of world writable files
print(WWFILE "$File::Find::name\n");
}
# Create the output file path if it doesn't already exist.
mkdir($dir or die "Cannot execute mkdir on ${dir}, $!") unless (-d $dir);
# Create our filehandle for writing our findings
open WWFILE, ">${dir}${tmpFile}" or die "Cannot open ${dir}${tmpFile}, $!";
print(WWFILE "${errHeader}") if ($errHeader);
finddepth(\&wanted, @mounts);
close WWFILE;
# If no world-writable files have been found ${tmpFile} should be zero-size;
# Delete it so Tivoli won't alert
if (-z "${dir}${tmpFile}") {
unlink "${dir}${tmpFile}";
} else {
rename("${dir}${tmpFile}","${dir}${permFile}") or die "Cannot rename file ${dir}${tmpFile}, $!";
}
在其他地方也有人建議我使用File :: Find :: Rule。 我寧願避免這樣做,因為我不想執行腳本的完全重寫。
就像我說過的,上面的腳本有效。 不過,我寧願不對排除項進行硬編碼。 弄清楚如何做到這一點,還可以讓我刪除與“特殊”目錄的匹配。
要修剪整個目錄樹,只需在wanted
子目錄中設置$File::Find::prune
值。 只要未指定bydepth
,這將起作用:
if ($File::Find::name eq '/opt/IBM/ITM') {
$File::Find::prune = 1;
return;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.