" <タスク> ジョブを追加する 例: add "*/10 * * * *" apfederate edit "<予定>" <タスク> ジョブを変更する 例: edit 1 "@hourly" apfederate enable|disable|toggle ジョブを有効・無効にする delete ジョブを削除する run ジョブを今すぐ実行する next [件数] 次の実行予定(既定5件) 予定は「分 時 日 月 曜日」又は @yearly @monthly @weekly @daily @hourly。 「*」がシェルに展開されない様に、予定は引用符で囲んで下さい。 TXT; $args = array_slice($argv, 1); $cmd = array_shift($args) ?? ''; /** 結果を表示して終了コードを返す */ $finish = function(Result $res): int { if ($res->isSuccess) { if ((string)$res->message !== '') echo $res->message, "\n"; return 0; } fwrite(STDERR, $res->message."\n"); return 1; }; /** 引数のID */ $id = function() use ($args, $usage): int { if (!isset($args[0]) || !ctype_digit($args[0])) { fwrite(STDERR, "IDを指定して下さい。\n\n{$usage}"); exit(2); } return (int)$args[0]; }; /** 引数のジョブ(未登録のタスクは警告だけ) */ $job = function(array $parts, bool $isActive = true) use ($cron, $usage): CronTab { try { $ct = CronTab::fromString(implode(' ', $parts), $isActive); } catch (\InvalidArgumentException $e) { fwrite(STDERR, $e->getMessage()."\n\n{$usage}"); exit(2); } if (!in_array($ct->command, $cron->tasks(), true)) { fwrite(STDERR, "注意:タスク「{$ct->command}」は登録されていません(util.phpのregister_cron_tasks()を参照)。\n"); } return $ct; }; $fmt = fn(?int $t) => $t === null ? '-' : date('Y-m-d H:i', $t); // 全角文字があっても列が揃う様に、表示幅で埋める $pad = fn(string $s, int $w) => $s.str_repeat(' ', max(1, $w - mb_strwidth($s))); switch ($cmd) { case '': // crontabはメールで出力を送るので、問題がなければ何も出力しない $res = $cron->run(); if (!$res->isSuccess) exit($finish($res)); foreach ($res->data['failed'] as $jid => $err) fwrite(STDERR, "#{$jid}:{$err}\n"); exit(empty($res->data['failed']) ? 0 : 1); case 'list': $res = $cron->list(); if (!$res->isSuccess) exit($finish($res)); if (empty($res->data)) { echo "ジョブはありません。\n"; exit(0); } $now = new \DateTimeImmutable(); $widths = [5, 5, 20, 16, 17, 17]; $row = function(array $cols) use ($pad, $widths): void { $line = ''; foreach ($widths as $i => $w) $line .= $pad((string)$cols[$i], $w); echo rtrim($line.$cols[count($widths)]), "\n"; }; $row(['ID', '有効', '予定', 'タスク', '次回', '前回', 'エラー']); foreach ($res->data as $d) { $ct = CronTab::fromArray($d); $err = $ct->validate(); $next = $err === null && $ct->isActive ? $ct->nextRun($now)?->getTimestamp() : null; $row([$ct->getId(), $ct->isActive ? 'o' : '-', $ct->expression(), $ct->command, $fmt($next), $fmt($ct->lastRun), $err ?? $ct->lastError ?? '']); } exit(0); case 'tasks': $tasks = $cron->tasks(); echo empty($tasks) ? "登録されたタスクはありません。\n" : implode("\n", $tasks)."\n"; exit(0); case 'add': exit($finish($cron->add($job($args)))); case 'edit': // 有効・無効はそのまま $old = $cron->show($id()); if (!$old->isSuccess) exit($finish($old)); exit($finish($cron->edit($id(), $job(array_slice($args, 1), (bool)($old->data['isActive'] ?? true))))); case 'enable': case 'disable': case 'toggle': case 'delete': exit($finish($cron->$cmd($id()))); case 'run': exit($finish($cron->runNow($id()))); case 'next': $res = $cron->show($id()); if (!$res->isSuccess) exit($finish($res)); $ct = CronTab::fromArray($res->data); if (($err = $ct->validate()) !== null) exit($finish(Result::Error($err))); $n = isset($args[1]) && ctype_digit($args[1]) ? max(1, min(100, (int)$args[1])) : 5; $t = new \DateTimeImmutable(); for ($i = 0; $i < $n && ($t = $ct->nextRun($t)) !== null; $i++) echo $t->format('Y-m-d H:i (D)'), "\n"; if ($i === 0) echo "5年以内に実行予定はありません。\n"; exit(0); case 'help': case '-h': case '--help': echo $usage; exit(0); default: fwrite(STDERR, "不明なコマンド:{$cmd}\n\n{$usage}"); exit(2); }