Eğer crontab -e açtıysanız ve görevinizin neden sabah saat 3'te çalışmadığını ya da bir betiğin neden saatte bir yerine her dakika çalıştığını bilmiyorsanız, sorun neredeyse her zaman alanların sırasındadır. Cron sözdiziminin beş alanı vardır ve sıraları sabittir; iki alanın yerini değiştirmek, zamanlamanın hiçbir hata vermeden başka bir anlama gelmesine neden olur. Cron size hiçbir şey söylemez. Sadece çalıştırmaz.
Cron sözdiziminin beş alanı ve her birinin izin verilen aralığı
crontab'daki her satır soldan sağa bu beş alanı okur, ardından komutu:
dakika saat ayın_günü ay haftanın_günü komut
0 3 * * * /usr/bin/php /var/www/backup.php
| Alan | Aralık | Anlam |
|---|---|---|
| dakika | 0–59 | Saatin dakikası |
| saat | 0–23 | Günün saati, gece yarısı sıfırdır |
| ayın günü | 1–31 | Takvim günü |
| ay | 1–12 | Ocak birdir, sıfır değil |
| haftanın günü | 0–7 | Sıfır ve yedi her ikisi de Pazar'dır |
En çok hataya yol açan iki nokta: ay birden başlar ama haftanın günü sıfırdan, ve 7 sayısı da Pazar'dır. Eğer birisi başka bir dilden ayı sıfırdan saymaya alışkınsa, zamanlaması bir ay kayar ve yıl sonuna kadar fark etmez.
Cron sözdiziminde operatörler ve özel ifadeler
Her alan şu dört biçimden birini alabilir:
- Yıldız (
*): Her izin verilen değer. Yani "her dakika" veya "her saat". - Liste (
1,15,30): Virgülle ayrılmış birkaç belirli değer. - Aralık (
9-17): İlk değerden son değere kadar, her iki uç dahil. - Adım (
*/15veya10-50/5): Her n birim. Dakika alanında*/15yani 0, 15, 30 ve 45.
Adımın aralıkla birleştirilmesi, çoğu kişinin hata yaptığı yerdir. Dakika alanındaki 10-50/5 ifadesi, 10. dakikadan başla ve 50'ye kadar her 5 dakikada bir ilerle demektir; sonuç 10, 15, 20 ve... olur. Ancak aralık olmadan 50/5, 50'den 59'a kadar demektir, "tüm saat boyunca her beş dakika" değil. Tüm saat boyunca her beş dakikada çalışmasını istiyorsanız, */5 yazın.
Ay ve haftanın günü adları
Sayı yerine İngilizce adların ilk üç harfini yazabilirsiniz: mon, tue, jan, dec. Büyük ve küçük harf fark etmez. Aralık da çalışır: mon-fri yani haftanın iş günleri.
Kısaltma ifadeleri
Cron'un beş alanın tamamını değiştiren sekiz makrosu vardır:
| İfade | Eşdeğeri |
|---|---|
@reboot | Bir kez, sistem açılırken |
@yearly / @annually | 0 0 1 1 * |
@monthly | 0 0 1 * * |
@weekly | 0 0 * * 0 |
@daily / @midnight | 0 0 * * * |
@hourly | 0 * * * * |
Bu ifadeler eski cron sürümlerinde desteklenmez. Eski bir sistemde çalışıyorsanız ve @daily sessizce yok sayılıyorsa, sayısal eşdeğerini yazın.
Yaygın kullanılan zamanlama örnekleri
| Cron sözdizimi | Çalışma zamanı |
|---|---|
*/5 * * * * | Her 5 dakika |
0 * * * * | Her saatin başında |
30 2 * * * | Her gün saat 02:30'da |
0 3 * * 0 | Pazar günleri saat 03:00'te |
0 0 1 * * | Her ayın birinci günü, gece yarısı |
0 9-17 * * 1-5 | Her saat, 9'dan 17'ye, Pazartesi'den Cuma'ya |
15 4 1,15 * * | Her ayın 1. ve 15. günleri, saat 04:15'te |
0 */6 * * * | Her 6 saat |
Veritabanı yedekleme gibi ağır işler için sabah 2 ile 4 arası doğru bir seçimdir çünkü site trafiği azdır. Ancak paylaşımlı hosting'deyseniz, bu saat sunucunun en yoğun zamanıdır; herkes yedeklerini oraya koymuştur. 01:20 veya 04:40 daha iyi bir seçimdir. Her görevin ne kadar kaynak tükettiğini görmek için, eksiksiz hosting kaynak limitleri referansı her sayının neyi saydığını açıklar.
Burada hata yapıyorlar
Ticket'larda gördüğüm en yaygın hata şudur: kullanıcı bir PHP betiğini 0 3 * * * php /home/user/backup.php ile zamanlar, görev çalışır, ama hiçbir şey olmaz. Belirgin işareti, /var/log/syslog içinde CRON[12345]: (user) CMD (php /home/user/backup.php) satırını görmenizdir, yani cron komutu çalıştırmıştır, ancak çıktı boştur. Nedeni neredeyse her zaman PATH'tir. Cron minimum bir ortamla çalışır; php onun PATH'inde değildir veya başka bir sürümdür. Çözüm: mutlak yol verin, /usr/bin/php8.2, ve crontab'ın başına bir PATH=/usr/local/bin:/usr/bin:/bin koyun.
Bulunması daha zor olan ikinci hata: betiğin çıktısı hiçbir yere gitmez. Cron her çıktıyı yerel kullanıcıya e-posta olarak gönderir. MTA kurulu değilse, mesaj kuyrukta kalır ve bir süre sonra /var/mail dolar. Her zaman komutun sonuna >> /var/log/backup.log 2>&1 koyun. Eğer bilerek hiçbir çıktı olmasını istemiyorsanız, >/dev/null 2>&1.
Dokümantasyonda bulamayacağınız bir nokta
Hem ayın günü hem de haftanın günü belirli bir değere sahip olduğunda, cron bunları AND değil OR ile birleştirir. Yani 0 0 1 * mon, "her ayın birinci günü, ya da her Pazartesi" demektir — "sadece Pazartesi'ye denk gelirse ayın birinci günü" değil. Bu davranış POSIX standardındadır ve çoğu uygulamada korunmuştur. Gerçekten "Pazartesi'ye denk gelen ayın birinci günü" istiyorsanız, betiğin kendisinde kontrol etmelisiniz veya OnCalendar=Mon *-*-01 ile systemd timer kullanmalısınız.
İkinci nokta: cron DST'den habersizdir. Eğer sunucu yerel saate ayarlıysa ve saat geri alınırsa, o saat aralığındaki görevler tekrarlı çalışır. Üretim sunucularında her zaman timedatectl set-timezone UTC yazın ve görev saatlerini UTC'ye göre yazın. Bu ayrıca logların sunucular arasında karşılaştırılmasını mümkün kılar.
Uzun süreli işler için cron yanlış seçimdir. Bir görev çalışma aralığından daha uzun sürerse, iki kopya aynı anda çalışır ve veritabanında kilitlenir. Basit çözüm: flock. Komutu şöyle yazın:
*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/bin/php /var/www/sync.php
-n anahtarı, kilit alınmışsa çalıştırma ve çık demektir. O olmadan flock bekler ve görev kuyruğu üst üste yığılır.
Cron mu systemd timer mı?
systemd'li bir özel sunucu veya VPS'de çalışıyorsanız ve yapılandırılmış log, diğer servislere bağımlılık veya kaynak kontrolüne ihtiyacınız varsa, systemd timer daha iyi bir seçimdir. Her çalıştırma journald'ye kaydedilir, tüm görevlerin aynı anda başlamaması için RandomizedDelaySec koyabilirsiniz ve Persistent=true ile kaçırılan görevler yeniden başlatmadan sonra telafi edilir. Cron'da bunların hiçbiri yoktur.
Ancak Linux hosting üzerinde günlük işlerin %90'ı için — temp dosyalarını temizleme, WordPress cron çalıştırma, log rotasyonu — cron daha basit ve yeterlidir. Sözdizimini bir kez öğrenirsiniz ve her yerde çalışır. Ben şahsen yapılandırılmış log veya servis bağımlılığına ihtiyaç duyana kadar timer'a başvurmam.
Paylaşımlı hosting'de karşılaşacağınız pratik bir kısıtlama: minimum çalışma aralığı genellikle 5 veya 15 dakikadır ve dakikalık görevler engellenir. Gerçekten her dakika çalıştırmaya ihtiyacınız varsa, mimariyi değiştirin: bir daemon yazın veya queue worker kullanın. Teşhis ve test araçlarını görmek için, ücretsiz webmaster araçları iyi bir başlangıç noktasıdır.
Sıkça sorulan sorular
Cron'un görevimi çalıştırıp çalıştırmadığını nasıl anlarım?
systemd'li sistemlerde, journalctl -u cron --since "1 hour ago" komutu her çalıştırmayı gösterir. Daha eski sistemlerde grep CRON /var/log/syslog işe yarar. Eğer CMD satırını görüyorsanız ama etkisi yoksa, sorun zamanlamada değil, çalışma ortamında veya betiğin kendisindedir.
Cron görevim neden doğru PATH ile çalışmıyor?
Cron minimum bir ortamla çalışır ve shell değişkenlerinizi okumaz. crontab'ın başında PATH değerini açıkça tanımlayın ve komutta binary'nin mutlak yolunu kullanın. Aynı durum DATABASE_URL gibi ortam değişkenleri için de geçerlidir; bunları crontab'da tanımlayın veya bir wrapper betiği kullanın.
Cron sözdiziminde */5 ile 0-59/5 arasındaki fark nedir?
Hiçbir farkı yoktur. Her ikisi de tüm saat boyunca her beş dakika demektir. Fark, aralığı sınırladığınızda ortaya çıkar: 10-50/5 yalnızca 10 ile 50 arasındaki dakikaları kapsar ve 55, 0 ve 5. dakikaları atlar.
Cron'u birden fazla sunucuda görev iki kez çalışmadan aynı anda çalıştırabilir miyim?
Sadece cron ile hayır. Cron'un dağıtılmış kilit mekanizması yoktur. Tek bir veritabanının arkasında birden fazla sunucunuz varsa, kilidi veritabanı katmanında veya Redis gibi bir araçla uygulamalısınız. Daha basit yol: görevi yalnızca bir sunucuda zamanlayın ve diğerleri onun sonucunu kullansın.
Şu anda bozuk bir crontab'ınız varsa, önce crontab -l ile mevcut içeriği görün, sonra crontab -e ile düzeltin. Kaydetmeden önce, beş alanı soldan sağa tekrar sayın. Bu tek alışkanlık, "cron çalışmıyor" ticket'larının çoğunu ortadan kaldırır.