使用 PHP 验证 Crontab 条目
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/235504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Validating Crontab Entries with PHP
提问by Wilco
What is the best way to validate a crontab entry with PHP? Should I be using a regex, or an external library? I've got a PHP script that adds/removes entries from a crontab file, but want to have some way to verify that the time interval portion is in a valid format.
使用 PHP 验证 crontab 条目的最佳方法是什么?我应该使用正则表达式还是外部库?我有一个 PHP 脚本,可以从 crontab 文件中添加/删除条目,但希望有一些方法来验证时间间隔部分的格式是否有效。
采纳答案by joelhardi
Hmmm, interesting problem.
嗯,有趣的问题。
If you're going to really validate it, regex isn't going to be enough, you'll have to actually parse the entry and validate each of the scheduling bits. That's because each bit can be a number, a month/day of the week string, a range (2-7), a set (3, 4, Saturday), a Vixie cron-style shortcut (60/5) or any combination of the above -- any single regex approach is going to get very hairy, fast.
如果你真的要验证它,正则表达式是不够的,你必须实际解析条目并验证每个调度位。这是因为每一位都可以是一个数字、一个月份/星期几的字符串、一个范围 (2-7)、一个集合 (3、4、星期六)、一个 Vixie cron 风格的快捷方式 (60/5) 或任何组合以上 - 任何单一的正则表达式方法都会变得非常多毛,很快。
Just using the crontabprogram of Vixie cron to validate isn't sufficient, because it actually doesn't validate completely! I can get crontabto accept all sorts of illegal things.
仅仅使用crontabVixie cron的程序来验证是不够的,因为它实际上并没有完全验证!我可以crontab接受各种非法的东西。
Dave Taylor's Wicked Cool Shell Scripts (Google books link) has a sh script that does partial validation, I found the discussion interesting. You might also use or adapt the code.
Dave Taylor 的 Wicked Cool Shell Scripts(Google 图书链接)有一个 sh 脚本可以进行部分验证,我发现讨论很有趣。您还可以使用或修改代码。
I also turned up links to two PHP classes that do what you say (whose quality I haven't evaluated):
我还打开了两个 PHP 类的链接,它们按照您说的做(我尚未评估其质量):
- http://www.phpclasses.org/browse/package/1189.html
- http://www.phpclasses.org/browse/package/1985.html
- http://www.phpclasses.org/browse/package/1189.html
- http://www.phpclasses.org/browse/package/1985.html
Another approach (depending on what your app needs to do) might be to have PHP construct the crontab entry programatically and insert it, so you know it's always valid, rather than try to validate an untrusted string. Then you would just need to make a "build a crontab entry" UI, which could be simple if you don't need really complicated scheduling combinations.
另一种方法(取决于您的应用程序需要做什么)可能是让 PHP 以编程方式构建 crontab 条目并将其插入,这样您就知道它始终有效,而不是尝试验证不受信任的字符串。然后你只需要制作一个“构建一个 crontab 条目”的 UI,如果你不需要非常复杂的调度组合,这可能很简单。
回答by Jordi Salvat i Alabart
Who said regular expressions can't do that?
谁说正则表达式不能做到这一点?
Courtesy of my employer, Salir.com, here's a PHPUnit test which does such validation. Feel free to modify & distribute. I'll appreciate if you keep the @author notice & link to web site.
由我的雇主Salir.com 提供,这里有一个 PHPUnit 测试可以进行此类验证。随意修改和分发。如果您保留@author 通知和网站链接,我将不胜感激。
<?php
/**
* @author Jordi Salvat i Alabart - with thanks to <a href="www.salir.com">Salir.com</a>.
*/
abstract class CrontabChecker extends PHPUnit_Framework_TestCase {
protected function assertFileIsValidUserCrontab($file) {
$f= @fopen($file, 'r', 1);
$this->assertTrue($f !== false, 'Crontab file must exist');
while (($line= fgets($f)) !== false) {
$this->assertLineIsValid($line);
}
}
protected function assertLineIsValid($line) {
$regexp= $this->buildRegexp();
$this->assertTrue(preg_match("/$regexp/", $line) !== 0);
}
private function buildRegexp() {
$numbers= array(
'min'=>'[0-5]?\d',
'hour'=>'[01]?\d|2[0-3]',
'day'=>'0?[1-9]|[12]\d|3[01]',
'month'=>'[1-9]|1[012]',
'dow'=>'[0-7]'
);
foreach($numbers as $field=>$number) {
$range= "($number)(-($number)(\/\d+)?)?";
$field_re[$field]= "\*(\/\d+)?|$range(,$range)*";
}
$field_re['month'].='|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec';
$field_re['dow'].='|mon|tue|wed|thu|fri|sat|sun';
$fields_re= '('.join(')\s+(', $field_re).')';
$replacements= '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';
return '^\s*('.
'$'.
'|#'.
'|\w+\s*='.
"|$fields_re\s+\S".
"|($replacements)\s+\S".
')';
}
}
回答by ph4r05
Thanks to Jordi Salvat i Alabart who posted great solution.
感谢 Jordi Salvat i Alabart 发布了很好的解决方案。
I have only modified existing solution posted by Jordi Salvat i Alabart. It worked for me well, but I wanted to extract particular parts by capturing groups. I have added non-capturing parentheses to be able to extract particular parts of crontab record. It is easy to see which capture group to use when you test output regex at: http://www.regexplanet.com/advanced/java/index.html
我只修改了 Jordi Salvat i Alabart 发布的现有解决方案。它对我很有效,但我想通过捕获组来提取特定部分。我添加了非捕获括号,以便能够提取 crontab 记录的特定部分。当您在以下位置测试输出正则表达式时,很容易看到要使用哪个捕获组:http: //www.regexplanet.com/advanced/java/index.html
<?php
/**
* @author Jordi Salvat i Alabart - with thanks to <a href="www.salir.com">Salir.com</a>.
*/
function buildRegexp() {
$numbers = array(
'min' => '[0-5]?\d',
'hour' => '[01]?\d|2[0-3]',
'day' => '0?[1-9]|[12]\d|3[01]',
'month' => '[1-9]|1[012]',
'dow' => '[0-6]'
);
foreach ($numbers as $field => $number) {
$range = "(?:$number)(?:-(?:$number)(?:\/\d+)?)?";
$field_re[$field] = "\*(?:\/\d+)?|$range(?:,$range)*";
}
$field_re['month'].='|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec';
$field_re['dow'].='|mon|tue|wed|thu|fri|sat|sun';
$fields_re = '(' . join(')\s+(', $field_re) . ')';
$replacements = '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';
return '^\s*(' .
'$' .
'|#' .
'|\w+\s*=' .
"|$fields_re\s+" .
"|($replacements)\s+" .
')' .
'([^\s]+)\s+' .
'(.*)$';
}
This code generates regex:
此代码生成正则表达式:
^\s*($|#|\w+\s*=|(\*(?:\/\d+)?|(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?(?:,(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?(?:,(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?(?:,(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?(?:,(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?)*|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+(\*(?:\/\d+)?|(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?(?:,(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?)*|mon|tue|wed|thu|fri|sat|sun)\s+|(@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly)\s+)([^\s]+)\s+(.*)$
Or Java alternative to generate this regex (without @X stuff):
或 Java 替代品来生成这个正则表达式(没有@X 的东西):
public static String buildRegex(){
// numbers intervals and regex
Map<String, String> numbers = new HashMap<String, String>();
numbers.put("min", "[0-5]?\d");
numbers.put("hour", "[01]?\d|2[0-3]");
numbers.put("day", "0?[1-9]|[12]\d|3[01]");
numbers.put("month", "[1-9]|1[012]");
numbers.put("dow", "[0-6]");
Map<String, String> field_re = new HashMap<String, String>();
// expand regex to contain different time specifiers
for(String field : numbers.keySet()){
String number = numbers.get(field);
String range = "(?:"+number+")(?:-(?:"+number+")(?:\/\d+)?)?";
field_re.put(field, "\*(?:\/\d+)?|"+range+"(?:,"+range+")*");
}
// add string specifiers
String monthRE = field_re.get("month");
monthRE = monthRE + "|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec";
field_re.put("month", monthRE);
String dowRE = field_re.get("dow");
dowRE = dowRE + "|mon|tue|wed|thu|fri|sat|sun";
field_re.put("dow", dowRE);
StringBuilder fieldsReSB = new StringBuilder();
fieldsReSB.append("^\s*(")
.append("$")
.append("|#")
.append("|\w+\s*=")
.append("|");
.append("(")
.append(field_re.get("min")).append(")\s+(")
.append(field_re.get("hour")).append(")\s+(")
.append(field_re.get("day")).append(")\s+(")
.append(field_re.get("month")).append(")\s+(")
.append(field_re.get("dow"))
.append(")")
.append("\s+)")
.append("([^\s]+)\s+")
.append("(.*)$");
return fieldsReSB.toString();
}
回答by Sergii Ivashchenko
There is a nice PHP library that can be used for Cron expression validation:
有一个不错的 PHP 库可用于 Cron 表达式验证:
To install this library via composer:
要通过 Composer 安装这个库:
composer require mtdowling/cron-expression
To check if Cron expression is valid
检查 Cron 表达式是否有效
$isValid = Cron\CronExpression::isValidExpression($expression);
回答by Lucas Oman
You should be able to do that fairly easily with regex. In fact, I wouldn't be surprised if you could find an existing regex for just that on Google. This is untested, but perhaps something like:
使用正则表达式应该可以很容易地做到这一点。事实上,如果你能在谷歌上找到一个现有的正则表达式,我不会感到惊讶。这是未经测试的,但可能类似于:
/^((\*)|(\d+((-\d+)|(,\d+)+))\s+){5}/
回答by Andrey
Thanks to Jordi Salvat i Alabart and ph4r05.
感谢 Jordi Salvat i Alabart 和 ph4r05。
I have small modified existing solution posted on php. Perl alternative to generate regex:
我在 php 上发布了小修改的现有解决方案。Perl 替代生成正则表达式:
sub _BuildRegex {
my $number = {
'min' => '[0-5]?\d',
'hour' => '[01]?\d|2[0-3]',
'day' => '0?[1-9]|[12]\d|3[01]',
'month' => '[1-9]|1[012]',
'dow' => '[0-6]'
};
my $field_re = {};
foreach my $nmb ( qw/min hour day month dow/ ) {
my $range = "(?:$number->{$nmb})(?:-(?:$number->{$nmb})(?:\/\d+)?)?";
$field_re->{$nmb} = "\*(?:\/\d+)?|$range(?:,$range)*";
}
$field_re->{'month'} .='|[jJ]an|[fF]eb|[mM]ar|[aA]pr|[mM]ay|[jJ]un|[jJ]ul|[aA]ug|[sS]ep|[oO]ct|[nN]ov|[dD]ec';
$field_re->{'dow'} .= '|[mM]on|[tT]ue|[wW]ed|[tT]hu|[fF]ri|[sS]at|[sS]un';
my $ff = [];
push @$ff, $field_re->{$_} foreach ( qw/min hour day month dow/ );
my $fields_req = '(' . join(')\s+(', @$ff) . ')';
my $replacements = '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';
return '^\s*(' .
'$' .
'|#' .
'|\w+\s*=' .
"|$fields_req\s+" .
"|($replacements)\s+" .
')' .
'([^\s]+)\s+' .
'(.*)$';
}
回答by night1008
Use the pattern:
/^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/
使用模式:
/^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/
In PHP:
在 PHP 中:
<?php
$cron = "*/5 1-2 3 3,4,5 *";
$result = preg_match( "/^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/", $cron, $matches);
print_r($matches);

