在Perl脚本中使用File :: Find时忽略整个目录

时间:2014-06-18 02:58:44

标签: perl filesystems

我有一个脚本,它扫描每个本地文件系统以查找世界可写文件。找到的所有文件都写入输出文件。它还使用另一个文件,该文件提供了要忽略的文件列表。

我们安装了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。我宁愿避免这样做只是因为我不想完全重写脚本。

正如我所说,上面的脚本有效。不过,我宁愿不对排除进行硬编码。弄清楚如何做到这一点也可以让我删除与“特殊”目录的匹配。

1 个答案:

答案 0 :(得分:3)

要修剪整个目录树,只需在$File::Find::prune子设置中设置wanted值即可。只要未指定bydepth,这将有效:

if ($File::Find::name eq '/opt/IBM/ITM') {
    $File::Find::prune = 1;
    return;
}