Disk Hog: 跟踪系统磁盘使用情况
系统管理员迟早都要执行的一项工作是实施磁盘配额策略。作为许多机器(主要是 Linux 和 Solaris,但也包括 AIX)的维护者,这些机器没有系统强制执行的配额,我需要一种自动跟踪磁盘配额的方法。为此,我创建了一个 Perl 脚本,定期检查用户的磁盘使用情况,并编译一个占用磁盘空间最多的用户列表。希望通过这种方式,我可以礼貌地劝说人们在他们的主目录变得过大时缩小其大小。
du 命令总结给定目录层次结构的磁盘使用情况。在每个用户的主目录中运行时,它会报告该目录占用了多少磁盘空间。起初,我编写了一个 shell 脚本,在许多用户目录中运行 du,并使用 awk 后端来提供良好的输出格式。当向系统中添加新用户时,这种方法被证明难以维护。不幸的是,用户的主目录位于每个操作系统上的不同位置。
Perl 提供了一种便捷的方法,可以将 shell/awk 脚本重写为单个可执行文件,这不仅提供了更强大的功能和灵活性,而且运行速度也更快。Perl 集成了标准的 Unix 系统调用和 C 库函数(例如 getpwnam() 和 getgrname()),使其非常适合此类任务。在本文中,我将描述如何使用 Perl 作为解决我特定需求的方案。我的 Perl 脚本的完整源代码可以通过匿名下载在文件 ftp://ftp.linuxjournal.com/pub/lj/listings/issue44/2416.tgz 中获得。
我做的第一件事是列出用户主目录所在的位置列表,并将此列表放入 Perl 数组中。对于此数组中目录的每个子目录,都需要磁盘使用情况摘要。此摘要是通过使用 Perl system 命令派生运行 du 的进程获得的。
du 输出被重定向到一个临时文件,使用了常见的 $$ 语法,该语法在运行时被执行进程的 PID 替换。这保证了我的磁盘使用情况脚本的多次调用(虽然不太可能)不会互相覆盖彼此的临时工作数据。
所有子目录都以拥有该帐户的用户的名字命名。这个假设使编写 Perl 脚本变得更容易一些,因为我可以跳过诸如 root、bin 等用户。
现在,在我的临时文件中,我有一个磁盘使用情况和用户名的列表,每行一对。我想将这些信息拆分为用户和磁盘使用情况的关联哈希,其中用户作为索引键。我还想保留整个磁盘使用情况和用户数量的运行总计。一旦 Perl 从临时文件中解析了所有这些信息,我就可以删除它。
我决定 Perl 脚本将其输出转储为 HTML 格式的页面。这使我在演示方面具有很大的灵活性,并允许信息通过本地 Intranet 提供——在处理多个异构环境时非常有用。
接下来,我必须决定需要呈现哪些信息。显然,脚本运行的日期很重要,并且按从大到小的磁盘使用量排序的表格是必不可少的。打印密码文件中的 GCOS(通用综合操作系统)信息字段使我可以查看真实姓名和用户名。我还决定提供指向用户主页的超链接(如果存在)。为此,我从密码文件中提取了他们的官方主目录,并向其添加了标准的用户目录扩展名(通常为 public_html 或 WWW)。
Perl 中的排序通常涉及使用“宇宙飞船”运算符 (<=>)。 sort 函数对列表进行排序并返回排序后的列表值。它有很多形式,但我代码中使用的形式是
sort sub_name list
其中 sub_name 是一个 Perl 子例程。 sub_name 在元素比较期间被调用,它必须返回一个小于、等于或大于零的整数,具体取决于列表元素的期望顺序。 sub_name 也可以替换为 Perl 代码的内联块。
通常,按升序进行数值排序的形式为
@NewList = sort { $a <=> $b } @List;
而按降序进行数值排序的形式为
@NewList = sort { $b <=> $a } @List;我决定通过添加一些无处不在的彩色球 GIF 来使页面更炫一些。绿色表示用户在允许的限制范围内。橙色表示用户处于危险缓冲区——无人区——他们非常接近红色区域。红球表示用户超出了配额,并且根据严重程度,可能会向真正贪婪的用户奖励多个红球。
最后,我使用所有 Web 搜索引擎进行搜索,直到找到合适的猪宝宝 GIF 图像,并将其放在页面顶部。
剩下的唯一工作是安排脚本每晚作为 cron 作业运行。此作业必须以 root 身份运行,以便准确评估每个用户的磁盘使用情况——否则目录权限可能会给出错误的结果。要编辑 root 的 cron 条目(称为 crontab),首先确保您已将环境变量 VISUAL(或 EDITOR)设置为您喜欢的编辑器,然后键入
crontab -e
将以下单行添加到任何现有的 crontab 条目
0 0 * * * /home/sysadm/ivan/public_html/diskHog.plcrontab 条目的格式很简单。前五个字段是整数,分别指定分钟 (0-59)、小时 (0-23)、月份中的日期 (1-31)、年份中的月份 (1-12) 和星期几 (0-6, 0=星期日)。允许使用星号作为通配符来匹配所有值,也允许指定以逗号分隔的元素列表或以起始和结束(以破折号分隔)指定的范围。第六个字段是要计划的实际程序。
这种大小的脚本(多次调用 du)需要一些时间来处理。因此,最好使用 cron 进行调度——我已将其设置为在大多数机器上每天运行一次(通常在夜间,用户活动较少时)。我认为这个脚本显示了使用 Perl、cron 和 WWW 来报告系统统计信息的潜力。我还编写了它的一个变体,用于分析 Web 服务器日志文件。这个脚本已经为我服务了许多个月,我相信它也会为其他系统管理员服务。
本文最初发表在 LinuxGazette.com 第 18 期中,LinuxGazette.com 是 Linux Journal 以前在线出版的电子杂志。

Ivan Griffin (ivan.griffin@ul.ie) 是爱尔兰利默里克大学 ECE 部门的研究生。他的兴趣包括 C++/Java、WWW、ATM、UL 计算机协会 (http://www.csn.ul.ie/),当然还有 Linux (http://www.trc.ul.ie/~griffini/linux.html)。