크론 스케줄 설명 스킬
Wikiprompt, 무료 프롬프트 백과사전에서
크론 스케줄 설명 스킬 크론 일정을 설명하고, 검증하며, 작성하는 완전한 AI 스킬입니다. Python 스크립트, 구문 참조, 함정 가이드, 검토 템플릿, 그리고 작업 예제를 포함합니다.
프롬프트 내용저장
🌐
---
name: cron-schedule-explainer
description: 크론 일정을 설명, 검증, 작성합니다 - 크론 표현식을 평이한 영어로 번역하고, 다음 실행 시간을 나열하며, 날짜-월 OR 날짜-요일 규칙, 발생하지 않는 날짜, 일광 절약 시간 공백, 시간대 혼동, 겹치거나 너무 잦은 작업과 같은 함정을 지적합니다. 사용자가 crontab 줄, Kubernetes CronJob, GitHub Actions 일정을 붙여넣거나 "이게 언제 실행되나요?" 또는 "매월 두 번째 화요일에 실행되는 크론을 작성해 주세요"라고 물을 때 사용합니다.
---
# 크론 일정 설명기
예약된 작업을 예측 가능하게 만듭니다. 모든 일정에 대해 평이한 영어 의미, 구체적인 다음 실행 시간, 그리고 새벽 2시에 누군가를 놀라게 할 위험을 제공합니다.
## 이 스킬의 파일
- `scripts/cron_explain.py` - 파서, 설명기, 다음 실행 계산기 및 함정 검사기 (Python 3 표준 라이브러리만 사용)
- `references/cron-syntax.md` - 필드 범위, 특수 문자, 매크로 및 플랫폼 차이
- `references/scheduling-pitfalls.md` - 일반적인 실수와 이를 피하는 방법
- `templates/schedule-review.md` - 검토 형식
- `examples/example-backup-review.md` - 세 개의 crontab 줄에 대한 작업 검토 예시
## 워크플로우
### 1. 플랫폼 식별
표준 5필드 크론 (Vixie cron, cronie, Kubernetes CronJob, GitHub Actions)이 기본값입니다. 사용자가 Quartz (초와 `?`가 있는 6 또는 7필드), AWS EventBridge (연도가 있는 6필드) 또는 systemd 타이머를 의미하는지 묻거나 확인하세요. `references/cron-syntax.md`를 참조하세요. 시간대를 기록하세요: GitHub Actions는 항상 UTC를 사용합니다. Kubernetes는 `timeZone`이 설정되지 않은 경우 컨트롤러의 시간대를 사용합니다.
### 2. 검사기 실행
```bash
python3 scripts/cron_explain.py "30 2 * * 1-5"
python3 scripts/cron_explain.py "0 9 1 * MON" --count 8 --from "2026-10-08 10:00"
python3 scripts/cron_explain.py --file crontab.txt
```
평이한 영어 설명, 다음 N개 실행 시간 (서버의 순수 로컬 시간) 및 경고를 출력합니다. 표현식이 유효하지 않거나 실행되지 않으면 종료 코드는 1입니다.
스크립트를 실행할 수 없으면 동일한 규칙을 수동으로 적용하고 그렇게 말하세요.
### 3. 설명
각 일정에 대해 다음을 제공하세요:
1. 한 문장의 평이한 영어 의미.
2. 시간대를 명시한 다음 3~5개 실행.
3. 스크립트 및 `references/scheduling-pitfalls.md`에서 적용되는 경고 (긴 작업과의 겹침, DST, UTC 대 로컬, 시스템이 꺼져 있는 동안 놓친 실행).
### 4. 일정 작성 또는 수정
사용자가 일정을 말로 설명하면 표현식을 작성한 다음 검사기를 통해 다음 실행이 의도와 일치하는지 확인하세요. 크론이 직접 표현할 수 없는 것 (매월 두 번째 화요일, 월의 마지막 평일)의 경우 크론 표현식과 명령의 가드를 제공하세요. 예: `[ "$(date +\%d)" -le 07 ] && run-job`, 그리고 이유를 설명하세요.
### 5. 보고
`examples/example-backup-review.md`에서와 같이 `templates/schedule-review.md`를 사용하세요.
## 규칙
- 항상 가정하는 시간대를 명시하세요.
- crontab 명령 필드에서 `%`는 `\%`로 이스케이프해야 한다는 점을 기억하세요.
- 사용자의 라이브 crontab을 직접 편집하지 마세요. 추가할 줄과 `crontab -e` 단계를 보여주세요.
- 작업이 간격보다 오래 실행될 수 있을 때마다 잠금 (예: `flock -n /tmp/job.lock cmd`)을 권장하세요.
FILE:references/cron-syntax.md
# 크론 구문 참조 (5필드 표준)
```
+------------- 분 (0-59)
| +----------- 시 (0-23)
| | +--------- 일 (1-31)
| | | +------- 월 (1-12 또는 JAN-DEC)
| | | | +----- 요일 (0-7 또는 SUN-SAT; 0과 7은 모두 일요일)
| | | | |
* * * * * 명령
```
## 특수 문자
| 기호 | 의미 | 예시 |
|---|---|---|
| `*` | 모든 값 | `* * * * *` 매분 |
| `,` | 목록 | `0 8,12,18 * * *` 08:00, 12:00, 18:00에 |
| `-` | 범위 | `0 9 * * 1-5` 월요일부터 금요일까지 09:00 |
| `/` | 단계 | `*/15 * * * *` 15분마다; `10-50/20` = 10, 30, 50 |
이름은 대소문자를 구분하지 않습니다. 이름 범위 (`MON-FRI`)는 대부분의 구현에서 작동하며, 이름 목록은 모든 곳에서 작동합니다.
## 매크로
| 매크로 | 동등 표현 |
|---|---|
| `@yearly` / `@annually` | `0 0 1 1 *` |
| `@monthly` | `0 0 1 * *` |
| `@weekly` | `0 0 * * 0` |
| `@daily` / `@midnight` | `0 0 * * *` |
| `@hourly` | `0 * * * *` |
| `@reboot` | 시작 시 한 번 (시간 기반 아님) |
## 일 규칙
일(day of month)과 요일(day of week)이 모두 제한된 경우 (둘 다 `*`가 아님), 작업은 둘 중 하나가 일치할 때 실행됩니다. `0 9 1 * MON`은 매월 1일과 모든 월요일에 실행됩니다.
## 플랫폼 차이
| 플랫폼 | 필드 | 시간대 | 참고 |
|---|---|---|---|
| Linux cron (cronie, Vixie) | 5 | 시스템 로컬 시간 | `CRON_TZ=`는 cronie에서 지원 |
| Kubernetes CronJob | 5 | 컨트롤러 시간대 또는 `spec.timeZone` | 겹침 방지를 위해 `concurrencyPolicy: Forbid` 사용 |
| GitHub Actions `schedule` | 5 | 항상 UTC | 부하 시 실행이 지연될 수 있음; 최소 간격 5분 |
| Quartz (Java) | 6-7 (초가 먼저, 선택적 연도) | 구성 가능 | `?`는 "특정 값 없음", `L`, `W`, `#` 지원 |
| AWS EventBridge | 6 (연도 포함) | 스케줄러 시간대가 설정되지 않으면 UTC | 일 또는 요일 중 하나는 `?`여야 함 |
이 스킬의 스크립트는 5필드 표준과 위의 매크로를 지원합니다 (`@reboot` 제외, 시간 기반이 아닌 것으로 보고).
FILE:references/scheduling-pitfalls.md
# 일정 계획 함정
## 1. 일(day of month) OR 요일(day of week)
`0 0 13 * 5`은 "13일의 금요일"이 아닙니다. 매월 13일과 모든 금요일에 실행됩니다. `0 0 13 * *`과 가드를 사용하세요: `[ "$(date +\%u)" = 5 ] && cmd`.
## 2. 발생하지 않거나 거의 발생하지 않는 날짜
- `0 0 30 2 *`는 절대 실행되지 않습니다 (2월에는 30일이 없음).
- `0 0 31 * *`은 1년 중 7개월에만 실행됩니다.
- `0 0 29 2 *`는 윤년에만 실행됩니다.
"월의 마지막 날"의 경우 `0 0 28-31 * *`과 가드를 사용하세요: `[ "$(date -d tomorrow +\%d)" = 01 ] && cmd`.
## 3. 일광 절약 시간 (DST)
DST가 있는 로컬 시간대에서 약 01:00에서 03:00 사이의 시간은 건너뛰거나 (봄 앞으로) 두 번 실행될 수 있습니다 (가을 뒤로, 크론 구현에 따라 다름). 중요한 작업은 해당 시간대 밖으로 예약하거나 크론을 UTC로 실행하세요.
## 4. UTC 대 로컬 시간
GitHub Actions 및 많은 클라우드 스케줄러는 UTC를 사용합니다. 이스탄불 (UTC+3) 팀의 "매일 09:00"은 UTC에서 `0 6 * * *`입니다. 문서 및 코드 주석의 표현식 옆에 항상 시간대를 작성하세요.
## 5. 너무 잦거나 겹치는 실행
- `* * * * *`는 하루에 1440번 실행됩니다. 의도된 것인지 확인하세요.
- 고정 시간과 분 필드 `*` (`* 3 * * *`)는 03:00에서 03:59 사이에 60번 실행됩니다. 일반적으로 `0 3 * * *`이 의도되었습니다.
- 작업이 간격보다 오래 걸릴 수 있으면 잠금 (`flock -n`) 또는 `concurrencyPolicy: Forbid`를 사용하세요.
## 6. 단계 값은 균등하게 나누어 떨어지지 않음
분 필드의 `*/7`은 0, 7, ..., 56에 실행된 다음 다시 0에 실행됩니다 (4분 간격). `*/25`는 0, 25, 50에 실행됩니다. 단계는 매시간, 매일 또는 매월 다시 시작됩니다.
## 7. 떼 몰림 (Thundering herd)
많은 팀이 `0 0 * * *` 또는 `0 * * * *`를 선택합니다. 공유 시스템 및 속도 제한 API의 부하 급증을 피하려면 작업을 특이한 분 (예: `17 2 * * *`)으로 이동하세요.
## 8. 환경 및 출력
크론은 최소한의 PATH와 로그인 셸 없이 실행됩니다. 절대 경로를 사용하고, crontab에서 필요한 변수를 설정하고, 출력을 리디렉션 (`>> /var/log/job.log 2>&1`)하여 실패가 보이도록 하세요. `%`를 `\%`로 이스케이프하세요.
## 9. 놓친 실행
일반 크론은 시스템이 꺼져 있는 동안 놓친 실행을 따라잡지 않습니다. 놓친 실행이 중요할 때는 anacron, `Persistent=true`가 있는 systemd 타이머 또는 Kubernetes `startingDeadlineSeconds`를 사용하세요.
FILE:templates/schedule-review.md
# 일정 검토: {{system_or_repo}}
**플랫폼:** {{Linux cron | Kubernetes CronJob | GitHub Actions | 기타}}
**가정된 시간대:** {{time_zone}}
**검토 날짜:** {{date}}
## 요약
{{한두 문장: 일정이 팀이 기대하는 대로 작동하는지, 무엇을 변경해야 하는지.}}
## 일정
### {{n}}. `{{expression}}` - {{job name}}
- **의미:** {{평이한 영어 설명}}
- **다음 실행:** {{run 1}}, {{run 2}}, {{run 3}}
- **판정:** {{OK | FIX | CLARIFY}}
- **경고:**
- {{warning}}
- **제안된 줄:**
```
{{수정된 crontab 줄}}
```
## 질문
- {{팀을 위한 질문}}
FILE:examples/example-backup-review.md
# 일정 검토: ops 서버 crontab
**플랫폼:** Linux cron (cronie)
**가정된 시간대:** Europe/Berlin (서버 로컬 시간, DST 있음)
**검토 날짜:** 2026-10-08
## 요약
세 줄 중 두 줄은 주석이 말하는 대로 작동하지 않습니다. 백업은 DST 창 안에서 실행되고, "13일의 금요일" 보고서는 실제로 모든 금요일과 모든 13일에 실행됩니다.
## 일정
### 1. `30 2 * * *` - 야간 데이터베이스 백업
- **의미:** 매일 02:30에.
- **다음 실행:** 2026-10-09 02:30, 2026-10-10 02:30, 2026-10-11 02:30
- **판정:** FIX
- **경고:**
- 02:30은 DST 변경 창 안에 있습니다. 봄 앞으로 전환하는 밤에는 건너뛸 수 있고 가을에는 두 번 실행될 수 있습니다.
- 백업은 월말에 한 시간 이상 걸릴 수 있습니다. 잠금이 없습니다.
- **제안된 줄:**
```
17 4 * * * flock -n /tmp/db-backup.lock /opt/scripts/db-backup.sh >> /var/log/db-backup.log 2>&1
```
### 2. `0 9 13 * FRI` - "13일의 금요일" 재미 보고서
- **의미:** 매월 13일 09:00 또는 모든 금요일 (크론의 일 규칙)에.
- **다음 실행:** 2026-10-09 09:00 (금), 2026-10-13 09:00 (화, 13일), 2026-10-16 09:00 (금)
- **판정:** FIX
- **경고:**
- 두 일 필드가 모두 제한되어 크론은 AND가 아닌 OR을 사용합니다.
- **제안된 줄:**
```
0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/scripts/fun-report.sh
```
### 3. `*/20 8-18 * * 1-5` - 헬프 데스크에서 티켓 동기화
- **의미:** 월요일부터 금요일까지 08:00에서 18:59까지 20분마다 (:00, :20, :40에).
- **다음 실행:** 2026-10-08 10:20, 2026-10-08 10:40, 2026-10-08 11:00
- **판정:** CLARIFY
- **경고:**
- 하루의 마지막 실행은 18:00이 아닌 18:40입니다. 동기화가 18:00에 중지되어야 하면 `8-17`과 별도의 `0 18 * * 1-5`를 사용하세요.
## 질문
- DST 문제를 완전히 피하기 위해 서버가 크론을 UTC로 실행해야 합니까?
FILE:scripts/cron_explain.py
#!/usr/bin/env python3
"""표준 5필드 크론 표현식을 설명, 검증 및 미리 보기 (표준 라이브러리만 사용).
사용법:
python3 cron_explain.py "EXPR" [--count N] [--from "YYYY-MM-DD HH:MM"]
python3 cron_explain.py --file crontab.txt [--count N] [--from ...]
각 표현식에 대해: 평이한 영어 설명, 다음 N개 실행 시간
(순수 서버 로컬 시간) 및 함정 경고. --file 모드에서는 crontab
줄을 읽습니다. 주석, 빈 줄 및 VAR=value 줄은 건너뛰고
처음 5개 필드 (또는 선행 @매크로)를 일정으로 간주합니다.
종료 코드: 0 = 모두 유효, 1 = 표현식이 유효하지 않거나 실행되지 않음, 2 = 사용법 오류.
"""
import argparse
import calendar
import datetime as dt
import sys
MONTHS = {m.lower(): i for i, m in enumerate(calendar.month_abbr) if m}
DAYS = {"sun": 0, "mon": 1, "tue": 2, "wed": 3, "thu": 4, "fri": 5, "sat": 6}
MACROS = {
"@yearly": "0 0 1 1 *", "@annually": "0 0 1 1 *", "@monthly": "0 0 1 * *",
"@weekly": "0 0 * * 0", "@daily": "0 0 * * *", "@midnight": "0 0 * * *",
"@hourly": "0 * * * *",
}
FIELDS = [("분", 0, 59, {}), ("시", 0, 23, {}), ("일", 1, 31, {}),
("월", 1, 12, MONTHS), ("요일", 0, 7, DAYS)]
DAY_NAMES = ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"]
def compress(values, fmt=str):
"""[1,2,3,5] -> '1-3, 5' 각 숫자에 fmt 사용."""
vals, out, i = sorted(values), [], 0
while i < len(vals):
j = i
while j + 1 < len(vals) and vals[j + 1] == vals[j] + 1:
j += 1
out.append(fmt(vals[i]) if j - i < 2 else f"{fmt(vals[i])}-{fmt(vals[j])}")
if 0 < j - i < 2:
out.append(fmt(vals[j]))
i = j + 1
return ", ".join(out)
class CronError(ValueError):
pass
def _num(token, lo, hi, names, field):
t = token.lower()
if t in names:
return names[t]
if not t.isdigit():
raise CronError(f"{field}: '{token}'은(는) 숫자 또는 알려진 이름이 아님")
v = int(t)
if not lo <= v <= hi:
raise CronError(f"{field}: {v}은(는) {lo}-{hi} 범위를 벗어남")
return v
def parse_field(text, lo, hi, names, field):
values = set()
for part in text.split(","):
if not part:
raise CronError(f"{field}: '{text}'에 빈 목록 항목")
step = 1
if "/" in part:
part, step_s = part.split("/", 1)
if not step_s.isdigit() or int(step_s) == 0:
raise CronError(f"{field}: 잘못된 단계 '/{step_s}'")
step = int(step_s)
if part == "*":
start, end = lo, hi
elif "-" in part:
a, b = part.split("-", 1)
start, end = _num(a, lo, hi, names, field), _num(b, lo, hi, names, field)
if start > end:
raise CronError(f"{field}: 범위 {a}-{b}가 역순")
else:
start = _num(part, lo, hi, names, field)
end = hi if step > 1 else start
values.update(range(start, end + 1, step))
return values
def parse(expr):
expr = expr.strip()
if expr.lower() == "@reboot":
raise CronError("@reboot는 시작 시 한 번 실행되며 시간 기반이 아님")
expr = MACROS.get(expr.lower(), expr)
parts = expr.split()
if len(parts) != 5:
hint = " (6-7필드는 Quartz 또는 EventBridge처럼 보임. references/cron-syntax.md 참조)" if len(parts) in (6, 7) else ""
raise CronError(f"5개 필드가 필요, {len(parts)}개 받음{hint}")
for p in parts:
bare = p.lower()
for n in list(MONTHS) + list(DAYS):
bare = bare.replace(n, "")
if any(c in bare for c in "?lw#"):
raise CronError(f"'{p}': '?', 'L', 'W' 및 '#'은 Quartz 확장이며 표준 크론이 아님")
sets = [parse_field(p, lo, hi, names, name) for p, (name, lo, hi, names) in zip(parts, FIELDS)]
if 7 in sets[4]:
sets[4].discard(7)
sets[4].add(0)
return parts, sets
def describe_set(values, lo, hi, field, raw):
vals = sorted(values)
if raw == "*":
return None
if field == "요일":
names = [DAY_NAMES[v] for v in vals]
if vals == [1, 2, 3, 4, 5]:
return "월요일부터 금요일"
if vals == [0, 6]:
return "주말"
return ", ".join(names)
if field == "월":
return ", ".join(calendar.month_name[v] for v in vals)
return compress(vals)
def explain(parts, sets):
minute, hour, dom, month, dow = sets
rm, rh, rdom, rmon, rdow = parts
if rm == "*" and rh == "*":
time_txt = "매분"
elif rm.startswith("*/") and rh == "*":
time_txt = f"매 {rm[2:]}분"
elif rm.startswith("*/"):
time_txt = (f"매 {rm[2:]}분 (분 " + ", ".join(str(m) for m in sorted(minute)) +
") 시 " + compress(hour, lambda h: f"{h:02d}") + " 동안")
elif rh == "*":
time_txt = "매시간 " + ", ".join(str(m) for m in sorted(minute)) + "분에"
elif rm == "*":
time_txt = "시 " + compress(hour, lambda h: f"{h:02d}") + " 동안 매분"
elif len(minute) * len(hour) <= 6:
time_txt = ", ".join(f"{h:02d}:{m:02d}" for h in sorted(hour) for m in sorted(minute)) + "에"
else:
time_txt = ("분 " + ", ".join(str(m) for m in sorted(minute)) +
"에 시 " + compress(hour, lambda h: f"{h:02d}") + " 후")
day_txt = []
d_dom = describe_set(dom, 1, 31, "일", rdom)
d_dow = describe_set(dow, 0, 6, "요일", rdow)
if d_dom and d_dow:
day_txt.append(f"월의 {d_dom}일 또는 {d_dow}에")
elif d_dom:
day_txt.append(f"월의 {d_dom}일에")
elif d_dow:
day_txt.append(d_dow if d_dow.startswith("on ") else f"{d_dow}에")
else:
day_txt.append("매일")
d_mon = describe_set(month, 1, 12, "월", rmon)
if d_mon:
day_txt.append(f"{d_mon}에")
text = f"{time_txt}, {' '.join(day_txt)}"
return text[0].upper() + text[1:] + "."
def day_matches(d, parts, sets):
_, _, dom, month, dow = sets
if d.month not in month:
return False
cron_dow = (d.weekday() + 1) % 7
dom_r, dow_r = parts[2] != "*", parts[4] != "*"
if dom_r and dow_r:
return d.day in dom or cron_dow in dow
if dom_r:
return d.day in dom
if dow_r:
return cron_dow in dow
return True
def next_runs(parts, sets, start, count, max_days=366 * 8):
minute, hour = sorted(sets[0]), sorted(sets[1])
runs = []
day = start.date()
for _ in range(max_days):
if day_matches(day, parts, sets):
for h in hour:
for m in minute:
t = dt.datetime(day.year, day.month, day.day, h, m)
if t > start:
runs.append(t)
if len(runs) >= count:
return runs
day += dt.timedelta(days=1)
return runs
def warnings(parts, sets, runs):
minute, hour, dom, month, dow = sets
out = []
if parts[2] != "*" and parts[4] != "*":
out.append("일(day of month)과 요일(day of week)이 모두 설정됨: 크론은 둘 중 하나가 일치할 때 실행됩니다 (OR, AND 아님).")
if parts[2] != "*" and parts[4] == "*":
max_days = {m: (29 if m == 2 else calendar.monthrange(2026, m)[1]) for m in month}
if not any(d <= max_days[m] for m in month for d in dom):
out.append("절대 실행되지 않음: 선택한 월에 선택한 일이 존재하지 않습니다.")
elif any(d > 28 for d in dom):
out.append("일부 선택된 날짜 (29-31)는 모든 월에 존재하지 않아 일부 월은 건너뜁니다.")
if parts[0] == "*" and parts[1] != "*":
out.append("분이 '*': 선택한 시간의 매분 실행됩니다. 분 0을 의미했습니까?")
runs_per_day = len(minute) * len(hour)
if runs_per_day >= 288:
out.append(f"하루에 {runs_per_day}번 실행됩니다. 의도된 것인지 확인하고 겹침 방지 잠금을 추가하세요.")
if any(1 <= h <= 2 for h in hour) and parts[1] != "*":
out.append("01:00에서 02:59 사이에 실행: DST가 있는 로컬 시간대에서는 건너뛰거나 두 번 실행될 수 있습니다.")
for i, raw in ((0, parts[0]), (1, parts[1])):
if "/" in raw:
step = int(raw.split("/")[1])
span = 60 if i == 0 else 24
if span % step:
out.append(f"단계 /{step}은(는) {span}을(를) 나누지 않음: {'시간' if i == 0 else '일'}이(가) 바뀌는 지점에서 간격이 고르지 않습니다.")
if parts[0] == "0" and parts[1] in ("*", "0"):
out.append("정시의 0분은 인기 있는 슬롯입니다. 부하 급증을 피하려면 특이한 분을 고려하세요.")
return out
def check(expr, start, count):
print(f"표현식: {expr}")
try:
parts, sets = parse(expr)
except CronError as e:
print(f" 유효하지 않음: {e}\n")
return False
print(f" 의미: {explain(parts, sets)}")
runs = next_runs(parts, sets, start, count)
ok = True
if runs:
print(f" {start:%Y-%m-%d %H:%M} 이후 다음 {len(runs)}개 실행 (서버 로컬 시간):")
for r in runs:
print(f" {r:%Y-%m-%d %H:%M} {r:%a}")
else:
print(" 다음 실행: 향후 8년 내에 없음")
ok = False
for w in warnings(parts, sets, runs):
print(f" 경고: {w}")
print()
return ok
def crontab_schedules(path):
with open(path, encoding="utf-8") as f:
for line in f:
s = line.strip()
if not s or s.startswith("#"):
continue
first = s.split()[0]
if "=" in first and not first.startswith("@"):
continue
yield first if first.startswith("@") else " ".join(s.split()[:5])
def main(argv=None):
ap = argparse.ArgumentParser(description="크론 표현식 설명 및 검증.")
ap.add_argument("expr", nargs="?", help='따옴표로 묶인 크론 표현식, 예: "*/15 9-17 * * 1-5"')
ap.add_argument("--file", help="crontab 파일에서 일정 읽기")
ap.add_argument("--count", type=int, default=5, help="표시할 다음 실행 수 (기본 5)")
ap.add_argument("--from", dest="start", help='시작 시간 "YYYY-MM-DD HH:MM" (기본: 현재)')
a = ap.parse_args(argv)
if bool(a.expr) == bool(a.file):
ap.print_usage(sys.stderr)
print("오류: EXPR 또는 --file 중 정확히 하나를 제공하세요.", file=sys.stderr)
return 2
try:
start = dt.datetime.strptime(a.start, "%Y-%m-%d %H:%M") if a.start else dt.datetime.now().replace(second=0, microsecond=0)
except ValueError:
print("오류: --from은 2026-10-08 10:00 형식이어야 합니다.", file=sys.stderr)
return 2
exprs = list(crontab_schedules(a.file)) if a.file else [a.expr]
results = [check(e, start, max(1, a.count)) for e in exprs]
print(f"{sum(results)} / {len(results)} 일정이 유효하고 실행 가능합니다.")
return 0 if all(results) else 1
if __name__ == "__main__":
sys.exit(main())
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 productivity와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개