如何在Solaris上的find命令中排除完整目录路径列表

4
我有一个非常具体的需求,需要使用脚本在Solaris中查找未拥有的文件和目录,并且需要能够从查找中排除完整的目录路径,因为它们包含可能有数千个未拥有的文件(这是正常的,因为它们是托管在其他服务器上的文件)。我甚至不希望find搜索这些目录,因为它会使服务器崩溃(CPU利用率长时间飙升到99%),因此将find结果通过egrep进行过滤以排除这些目录不是一个选项。
我知道可以通过以下方式排除一个或多个目录名称: find / -mount -local \( -type d -a \( -name dir1 -o -name dir2 -o dir3 \) \) -prune -o \( -nouser -o -nogroup \) -print 然而,这将匹配任何目录结构中的dir1和dir2,这根本不是我想要的。
我希望能够防止find甚至搜索以下目录(作为示例):
/opt/dir1
/opt/dir2
/var/dir3/dir4

我希望它能够在以下目录中查找未被拥有的文件和目录:

/opt/somedir/dir1
/var/dir2
/home/user1/dir1

我尝试在-name参数中使用正则表达式,但由于find仅将“name”与所找到的内容的基本名称匹配,因此我无法指定路径。不幸的是,Solaris的find不支持GNU find选项,如-wholename或-path,所以我有些束手无策。
我的目标是编写一个具有以下语法的脚本:
script.sh "/path/to/dir1,/path/to/dir2,/path/to/dir3"
在Solaris(5.8及以上版本)上使用find和标准sh脚本(/ bin / sh),我该如何实现这一点?

1
从'/'开始查找是有问题的。在脚本中添加一个预处理步骤,仅获取顶级路径元素,即/opt、/var,然后仅针对这些目录运行find命令,在排除不感兴趣的目录后进行操作?这只是一个想法,祝你好运!如果您有自己的解决方案,请发布,因为这是一个有趣的问题。如果您在http://unix.stackexchange.com/上发布,您可能会得到更多的关注。 - shellter
从/开始实际上是必需的。此脚本的目的是在我们为客户支持的服务器中列出所有未拥有的文件和目录,而不知道它们可能在哪里。我们确定有一些目录我们永远不想扫描(/proc、/dev、远程挂载等...),但我们总是需要扫描其他所有内容。我们仔细考虑了使用包含列表而不是排除列表的方法,但我们别无选择,只能选择排除选项,因为它涵盖了最多的未知情况(还跟上我吗?)。 - Yanick Girouard
刚刚按照建议在unix.stackexchange.com上复制了这篇文章:http://unix.stackexchange.com/questions/23077/how-to-exclude-a-list-of-full-directory-paths-in-find-command-on-solaris - Yanick Girouard
rozcietrzewiacz 在unix.stackexchange.com上发布了一个潜在的答案:“由于find的实现不支持-path测试,因此您可以使用-exec test "{}" = "/path/to/exclude" \; -prune来模拟它。 {}应扩展为完整路径名。”我测试了这个解决方案,虽然它需要更多的处理能力,但它有效。如果我要针对文件服务器运行这个解决方案,我担心会增加处理时间和CPU使用率... 不过我还是考虑采纳这个答案。你对此有什么看法? - Yanick Girouard
1个回答

3
您无法使用Solaris find通过完整路径匹配文件,但是您可以通过inode匹配文件。因此,请使用ls -i生成要修剪的inode列表,然后调用find。这假定您要修剪的目录不多到越过命令行长度限制。
inode_matches=$(ls -bdi /opt/dir1 /opt/dir2 /var/dir3/dir4 |
                sed -e 's/ *\([0-9][0-9]*\) .*/-inum \1 -o/')
find / -xdev \( $inode_matches -nouser -o -nogroup \) -prune -o -print

另一种方法是使用Perl或Python脚本并自行编写目录遍历。Perl附带一个find2perl脚本,可以让您开始使用File::Find模块。在Python中,请参见os.path模块中的walk函数。


我会测试一下并告诉你它的运行情况。别担心,如果这个方法有效,我会很高兴地接受答案! - Yanick Girouard
非常好用!但是为了我的目的,我用awk替换了sed:exclude_dirs_inums=ls -bdi $exclude_dirs 2> /dev/null | awk '{print $1}' | tr '\n' ',' | sed -e 's/,$//' | sed -e 's/,/ -o -inum /g',然后我用这个表达式填充的变量是f_exclude_dirs="( -inum $exclude_dirs_inums ) -prune -o"`。(对于格式化不好的问题,但是我的代码中有太多的反引号...) - Yanick Girouard
@YanickGirouard 提示:使用 $(...)。不仅更容易与 markdown 协调,而且在替换命令中有引号时也可以避免头痛。您似乎要经历很多困难才能避免最后的 -o。将 -e '$s/-o//' 添加到 sed 的参数中会更简单。 - Gilles 'SO- stop being evil'
谢谢Gilles。实际上,我非常清楚这一点。我不记得在何时何地,但我曾经在一些旧系统上使用/bin/sh和$()时遇到过问题。它无法解析,因为sh的版本太旧了(可能是Solaris 5.8)。当然,我也更喜欢使用$(),通常使用bash,但是这个脚本必须在数百个系统上可移植,其中一些已经有几年历史了。我必须使用我知道在任何情况下都能正常工作的方法。 - Yanick Girouard
抱歉 Gilles,我第一次错过了你评论的最后一部分。-e '$s/-o//' 到底是做什么的?我不熟悉这种形式。是修剪最后一个吗?我不知道你可以在 s 前面使用匹配字符。 - Yanick Girouard
@YanickGirouard 早期的 Bourne shell 没有 $(...),但是现在生产环境中默认的 sh 已经不再使用这么老的 shell 了。$s/-o// 只会在最后一行输入上应用 s/-o// - Gilles 'SO- stop being evil'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接