更佳编程之路: 第 11 章. 利用 cfperl 进行 crontab 管理_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3780 | 回复: 0   主题: 更佳编程之路: 第 11 章. 利用 cfperl 进行 crontab 管理        下一篇 
谁是天蝎
注册用户
等级:大元帅
经验:90210
发帖:106
精华:0
注册:2011-7-21
状态:离线
发送短消息息给谁是天蝎 加好友    发送短消息息给谁是天蝎 发消息
发表于: IP:您无权察看 2011-8-25 16:40:39 | [全部帖] [楼主帖] 楼主

正如文章标题所示,本文是正在连载的系列文章中的一部分。建议您阅读本系列 以前发表的章节,以了解 cfperl 的背景知识、理论基础和结构。

使用 cfperl 进行 crontab 管理是很理想的,这样就可以用纯英语来描述 crontab 任务。cfperl 语法可以用一行来描述标准 crontab 中要用多行描述的内容(例如,那些在 5:30 和 6:00 运行的任务)。cfperl 使用标准的 cron 扩展来实现其 crontab 功能。

cfperl crontab 部分是用标准 cfperl 方式解析的,通过顶级解析器将“cron”部分指向特定的主机、组等等。

什么是 crontab?

cron 包

由两个程序构成:定期执行任务的

cron 守护程序

,以及修改个人用户的 cron 任务列表的

crontab 程序

。这两个程序非常有用,如果您使用过 UNIX 系统,那么您可能听说过 crontab。用户的 cron 任务列表被称为

crontab

,它就象用来修改该列表的程序一样。我将讨论标准的 cron 包,但不会讨论由于 cron 重写而添加的所有扩展。经过改进的 cron 包(如 anacron、fcron 和 ucron)要优于标准的 cron,但遗憾的是,它们都对标准的 crontab 格式进行了非标准的扩展。

在典型的 UNIX 风格中,标准的 crontab 格式非常有效率,但是非常不便于阅读。六个字段(分钟、小时、日、月、星期几和命令)是用空格分隔的。每个字段都有一个列数字或“*”;例如小时字段中的“0,1,2”意味着该命令应当在午夜 0 点、1 点和 2 点运行。当某个字段中出现“*”时,这意味着 cron 应当匹配该字段中的任何内容 — 任何小时、任何天、任何月等等。另外一个复杂之处在于,星期几是从 0 开始计数的(星期天是 0),而每月的天数是从 1 开始计数的(从 1 到 31)。

星期几字段稍微有点复杂,因为它不考虑日字段。如果日是“*”而星期几是“0”,那么命令将只在星期天运行。如果日是“1”,而星期几是“*”,那么命令将只在该月的第一天运行。但是,如果日是“5”而星期几是“2”,那么命令将在该月的第 5 天和星期二

运行。

幸运的是,cfperl 使您无须记住所有那些东西 — 除非您对那类问题很感兴趣,如果是那样的话,cfperl 的 crontab 功能对您而言是毫无用处的,也不必用它来解决问题。它不是一种“爱出风头”的软件。



在使 crontab 更易于管理方面,cfperl 能做些什么?

利用 cfperl,可以按照便于阅读的规范生成 crontab。例如:

hourly at 0,10,20,30,40,50 do as cftest /usr/bin/synchronize


很简单,是吗?您必须做的所有工作就是指定作业执行的分钟字段,以及将编辑哪个用户(在本例中是“cftest”)的 crontab。这很容易地转换成了一行 crontab:

0,10,20,30,40,50 * * * * CFPERL=1 /usr/bin/synchronize


请注意“CFPERL=1”部分;它向后续运行的 cfperl 指出:该行是由 cfperl 生成的,可以重新生成。

范围怎么样呢?例如,为 cfperl 提供的分钟范围“23-43,44-49”,在 crontab 中将生成“23-49”。



用户规范解析器

用于 crontab 项用户规范的解析器是 %parsers 散列(带有键“cron”)的成员。按照 cfperl 的惯例,cfperl 配置中的“cron”部分将调用该解析器。

该解析器可以处理三种请求:删除(delete)、删除全部(deletfull)和常规(regular)crontab 请求。如果 cfperl 要清除用户的 crontab 中的旧项或全部 crontab 项,则可以使用删除。

cfperl“cron”解析器中的 crontab 请求是根据频率规范(“yearly”、“monthly”、“daily”和“hourly”)构建的。

清单 1. 顶级“cron”解析器频率规范

frequency: yearly | monthly | weekly | daily | hourly
hourly: /hourly/i /at/i minute { { minute => $item{minute} } }
daily: /daily/i /at/i hour { { hour => $item{hour} } }
weekly: /weekly/i weektime_spec_list { { weektime => $item{weektime_spec_list} } }
monthly: /monthly/i monthtime_spec_list { { monthtime => $item{monthtime_spec_list} } }
yearly: /yearly/i yeartime_spec_list { { yeartime => $item{yeartime_spec_list} } }


其余规则相当简单,这些规则提供了几乎所有种类的递归事件的详尽声明。但是,数字项和范围的规范略微复杂,需要在专门的一节中进行介绍。



数字项和范围

针对“cron”部分的解析器需要理解任何数字范围或项。这包括小时,由于分钟是额外的负担,因此解析小时稍微有点困难。

清单 2. 数字项和范围规范

minute: numeric_list
hour: numeric_hour_list
day: numeric_list
month: numeric_list
weekday: numeric_list
numeric: numeric_range | numeric_single
numeric_single: /\d+/
numeric_hour: /\d+/ ':' /\d\d/
{ { hour => $item[1], minutes => $item[3] } }
| numeric_single
{ { hour => $item{numeric_single}, minutes => 0 } }
numeric_range: numeric_single '-' numeric_single
{ $return = [ ($item[1] .. $item[3]) ]; 1; }
numeric_hour_list: numeric_hour ',' numeric_hour_list
{ $return = [ @{$item{numeric_hour_list}},
(ref $item{numeric_hour} eq 'ARRAY') ?
@{$item{numeric_hour}} : $item{numeric_hour} ];
       1; }
       | numeric_hour
{ $return = [ (ref $item{numeric_hour} eq 'ARRAY') ?
       @{$item{numeric_hour}} : $item{numeric_hour} ];
             1; }
             | '*'
            numeric_list: numeric ',' numeric_list
       { $return = [ @{$item{numeric_list}},
                   (ref $item{numeric} eq 'ARRAY') ?
                   @{$item{numeric}} : $item{numeric} ];
                         1; }
                         | numeric
                   { $return = [ (ref $item{numeric} eq 'ARRAY') ?
                         @{$item{numeric}} : $item{numeric} ]; 1; }
                                     | '*'
                                    


用于“numeric_list”和“numeric_hour_list”规则的解析器规范是递归的。规则取决于其本身或 终结符

,这意味着最终它必须找到匹配规则交替(rule alternation)的文本,该规则交替不取决于规则本身。

如果觉得这让您困惑,那么请考虑一列数字,例如“5,6,7”。您可以将该列表定义成“(1)一个数字,后面跟着一个逗号,逗号后面再跟着一个数字列表,或者(2)数字本身”。对于解析一列数字的一般规则,规则(1)和规则(2)是可以交替使用的解决方案。但是仅使用规则(2)是无用的,因为它只理解单个数字。另一方面,规则(1)不足以处理列表结尾处的问题,因为其尾端无逗号。如果要递归地解析数字列表,那么必须交替使用规则(1)和规则(2)。

解析项列表有其它许多方法,但是利用 Parse::RecDescent (它是递归的子代解析器),递归方法的效果是最佳的。它还是一种非常完美的方法,用几行就概括了对列表的所有可能的解释。请参考 Parse::RecDescent 文档,以获取更多有关递归解析器和规则的信息(请参阅 参考资料以获取链接)。

复杂性的另一面在于平铺(flatten)数组引用的代码。在某些地方,如果项的内容是数组引用,那么它们会被区别对待。这是因为将数组引用平铺成列表可以在解析器中完成。

数组引用本应当保留在解析器输出中,将由使用该解析器的代码进行解释。但是,将引用平铺成列表只会使解析器变得更为复杂一些,而必须处理作为输出的引用的代码则会变得相当复杂。

我们感兴趣的一个规则就是“星期几”规则。

清单 3. 星期几

weekday_name: 'Monday' { 1 } | 'Tuesday' { 2 } | 'Wednesday' { 3 } |
'Thursday' { 4 } | 'Friday' { 5 } | 'Saturday' { 6 } |
'Sunday' { $return = 0; 1; }


该规则对 Sunday 进行区别对待,因为在 crontab 格式中它是 0。这很适合 crontab,但对于解析器而言却很槽糕。如果简单地用 0 表示星期天,那么解析器会认为 0 表示解析出错。因此,我们将 $return 变量设为 0,但是根据规则却返回 1。这是 Parse::RecDescent 的一种常见且危险的隐患,其中,如果根据规则直接返回,那么正确解析的 0 就会被认为是一种错误。



用于 crontab 生成的 Perl 功能

“cron”解析器的所有输出都被传递给 cron_op() 函数。我们对辅助函数 get_cron_lines() 、 put_cron_lines() 和 get_cron_command() 不感兴趣;这些函数只是读或写 crontab,并获取正确的方法以调用 crontab 命令(正如前面讨论的,该命令由 cron 守护程序使用)。

通过使用 make_cron_line() 来执行机械的工作, construct_cron_line() 函数构建了一行实际的 cron 项。

extract_yearly_intervals() 、 extract_weekly_or_monthly_intervals() 和 extract_daily_intervals() 函数使用 reduce_interval() 来获取其时间间隔的最短表示,但是,即使可以使用“*”,它们也不使用该符号。这不是一个特性,也不是一个错误,但在以后会得到修复。

此外, extract_daily_intervals() 尝试合并时间间隔。这意味着,如果用户要求某个命令在每天上午 5:30 和下午 3:30 执行,那么 extract_daily_intervals() 将认为只要一行 crontab(“30 5,15 * * * COMMAND”)就足以实现用户的请求。不相容的时间间隔(例如上午 3:30 和下午 4:20)将作为单独的 crontab 行完成。现在还无法修复该问题;它是标准的 crontab 格式所带来的基本问题。

我将只合并每天的时间间隔。尽管也可以合并每星期、每月和每年的时间间隔,但是我觉得现在不值得花工夫去研究。但是肯定是可以完成。



结束语

cfperl 的 crontab 能力是解析用户输入和生成用标准格式表示的 crontab 项的简单组合。通过 Parse::RecDescent 的解析能力,cfperl“cron”部分语法支持各种自由格式的、用于递归事件的用户规范。该语法是可扩展的,并且也便于维护。

我们做了两个折衷。一个折衷是当生成新的 crontab 时,丢弃以前由 cfperl 生成的 crontab 行。实际上,每次都会重新生成由 cfperl 生成的那部分 crontab。除了 cfperl 运行时花费了一些额外的处理时间之外,这样做对于用户并没有显著的影响。可替代的方法是使用顺序标记、校验和或其它标识措施来识别需要重新生成的 crontab 行,但是该方法有两个问题。第一个问题就是该方法是一种投入小于产出的方法。另一个问题就是由 cfperl 生成的旧项会使 crontab 变得杂乱无章,其结果是:已经不再需要某些旧项,但却没人敢把它们除去。

第二个折衷就是不合并一天以上的时间间隔。这意味着不会自动将某个规范,例如“monthly on 1 at 2; on 4 at 2”,合并成一条“monthly on 1,4 at 2”规范。可以添加这一功能,但是它唯一的好处是减少生成的 crontab 行数,其回报相对于必须花费的额外工作似乎不划算。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论