2008-10-24 3 views
11

Каков наилучший способ проверки записи crontab с помощью PHP? Должен ли я использовать регулярное выражение или внешнюю библиотеку? У меня есть PHP-скрипт, который добавляет/удаляет записи из файла crontab, но хочет иметь некоторый способ проверить, что часть интервала времени находится в допустимом формате.Проверка записей Crontab с PHP

ответ

7

Хммм, интересная проблема.

Если вы собираетесь его проверить, регулярное выражение не будет достаточным, вам нужно будет проанализировать запись и проверить каждый бит планирования. Это связано с тем, что каждый бит может быть числом, месяцем/днем ​​недели, диапазоном (2-7), набором (3, 4, суббота), ярлыком стиля Vixie cron (60/5) или любой комбинацией из вышеперечисленного - любой подход с одним регулярным выражением будет очень волосатым, быстрым.

Просто использовать программу Vixie cron для проверки не достаточно, потому что она фактически не проверяется полностью! Я могу получить crontab, чтобы принять всевозможные незаконные вещи.

Dick Taylor's Wicked Cool Shell Scripts (Google books link) имеет скрипт sh, который выполняет частичную валидацию, я нашел интересную дискуссию. Вы также можете использовать или адаптировать код.

Я также появился ссылки на два класса PHP, что делать то, что вы говорите (качество которых я не оценивали):

Другой подход (в зависимости от то, что должно делать ваше приложение) может быть, чтобы PHP построил запись crontab программно и вставлял ее, поэтому вы знаете, что она всегда действительна, а не пытается проверить ненадежную строку. Тогда вам просто нужно создать пользовательский интерфейс «build a crontab entry», который может быть простым, если вам не нужны действительно сложные комбинации планирования.

1

Вы должны иметь возможность сделать это довольно легко с помощью регулярного выражения. На самом деле, я бы не удивился, если бы вы могли найти существующее регулярное выражение только для Google. Это не проверено, но возможно что-то вроде:

/^((\*)|(\d+((-\d+)|(,\d+)+))\s+){5}/ 
+0

это не соответствует `@ reboot`,` @ daily`, `2- 39/4` и несколько других расширений Vixie – Jasen 2015-01-10 04:59:58

21

Кто сказал, что регулярные выражения не могут это сделать?

Предоставлено моим работодателем, 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". 
      ')'; 
    } 
} 
+0

Это замечательно! В моем случае я хочу проверять полное содержание crontab по строкам. Поэтому я просто добавляю несколько проверок перед запуском вышеупомянутого решения, чтобы также проверять пустые строки, строки, начинающиеся с комментария #, и строки с присваиванием переменных «ABC = DEF» – bksunday 2017-08-13 16:53:08

3

Благодаря Jordi Salvat i Alabart, который опубликовал большое решение.

У меня есть только модифицированное существующее решение, отправленное 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+' . 
      '(.*)$'; 
} 

Этот код генерирует регулярное выражение:

^\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+(.*)$ 

Или 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(); 
} 
+0

Не знаю, сгенерированное регулярное выражение не будет соответствовать даже простым `* * * * * `. Что я делаю неправильно? https://regex101.com/r/rO2wW9/2 – bentinata 2015-03-31 08:10:14

0

Благодаря Jordi Salvat i Alabart и ph4r05.

У меня есть небольшое модифицированное существующее решение, размещенное на 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+' . 
     '(.*)$'; 
} 
0

Используйте шаблон: /^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/

В 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); 
0

Существует хорошая PHP библиотека, которая может быть использована для проверки экспрессии Cron:

Чтобы установить эту библиотеку с помощью композитора:

composer require mtdowling/cron-expression 

Чтобы проверить, если выражение Cron справедливо

$isValid = Cron\CronExpression::isValidExpression($expression); 
Смежные вопросы