Bagaimana saya bisa memulai cronjob 1 jam kemudian setiap hari?

16

Saya perlu memulai cronjob setiap hari, tetapi satu jam kemudian setiap hari. Apa yang saya miliki sejauh ini berfungsi sebagian besar, kecuali untuk 1 hari dalam setahun:

0 0 * * * sleep $((3600 * (10#$(date +\%j) \% 24))) && /usr/local/bin/myprog

Ketika hari tahun 365, pekerjaan akan dimulai pukul 5:00, tetapi hari berikutnya (tidak termasuk tahun kabisat) akan memiliki hari dalam setahun sebagai 1, sehingga pekerjaan akan dimulai pada 1:00. Bagaimana saya bisa menyingkirkan kasing sudut ini?

Birch
sumber
1
Adakah alasan untuk tidak memulainya setiap 25 jam?
HalosGhost
7
Dan bagaimana tepatnya Anda melakukan itu? * / 25 di posisi jam tidak akan menyelesaikannya.
birch
@HalosGhost Terima kasih atas saran Anda! Saya menulis implementasi sederhana berdasarkan pada.
Giulio Muscarello

Jawaban:

23

Solusi pilihan saya adalah memulai pekerjaan setiap jam tetapi minta skrip itu sendiri memeriksa apakah sudah waktunya untuk menjalankan atau tidak dan keluar tanpa melakukan apa pun 24 kali dari 25.

crontab:

0 * * * *    /usr/local/bin/myprog

di bagian atas myprog:

[ 0 -eq $(( $(date +%s) / 3600 % 25 )) ] || exit 0

Jika Anda tidak ingin membuat perubahan apa pun pada skrip itu sendiri, Anda juga dapat memberi tanda "waktu untuk menjalankan" di entri crontab tetapi itu membuat garis panjang yang tidak enak dilihat:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog
Celada
sumber
1
'% perlu lolos dengan backslash di crontab.
birch
@ birch, aku tidak pernah tahu itu! Saya kira saya belum pernah mencoba memasukkan% dalam crontab. Terima kasih atas koreksinya, saya telah mengedit jawabannya.
Celada
1
Ini terlihat bagus untukku. Saya tidak tahu apa yang ingin dilakukan OP sehubungan dengan Daylight Savings Time (musim semi depan, jauh di belakang), tetapi OP harus membuat penyesuaian yang sesuai.
emory
Saya akan sedikit khawatir tentang pembulatan di divisi. Jika karena alasan tertentu waktu yang datekembali dari kernel 1mssebelum waktu yang Anda harapkan skrip dijalankan, cek akan memberikan hasil yang salah.
kasperd
1
@kasperd Saya tidak tahu, saya kira Anda mungkin benar tentang berbagai CPU yang melaporkan waktu yang berbeda. Adapun ntpd, itu berusaha keras untuk hanya mematikan jam, bukan melompat, khususnya untuk menghindari masalah semacam ini, tetapi Anda benar, ntpdatekadang-kadang (atau ) kadang-kadang dapat melompat mundur waktu. Adapun cronsalah perhitungan penundaan tidurnya, saya cukup yakin itu akan dianggap bug! Namun, poin yang diambil, dan solusinya adalah untuk menjadwalkan pekerjaan 30 menit melewati jam yang cenderung menyebabkan masalah ... Atau tambahkan ± 1800 dalam ekspresi aritmatika sebelum mengambil reamainder mod 3600.
Celada
7

Jika sistem Anda memiliki systemd, Anda dapat menggunakan event timer untuk ini. Cukup tentukan layanan baru , yang harus berisi perintah / tugas yang ingin Anda jalankan, dan kemudian buat acara pengatur waktu dengan OnUnitActiveSecopsi:

[Unit]
Description=daily + 1 hour task

[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target

Gunakan nama yang sama untuk file, kecuali bahwa bukan .serviceAnda gunakan .timer.

Mensintesis:

  1. Buat file yang disebut job.servicedi /etc/systemd/system/direktori.
  2. Isi dengan informasi yang diperlukan. Anda dapat memverifikasi konfigurasi, menggunakan systemctl status job.service.
  3. Buat file bernama job.timerdalam /etc/systemd/system/.
  4. Isi dengan informasi yang diperlukan:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Verifikasi timer menggunakan systemctl list-timers
  6. Selesai
Braiam
sumber
Jika saya akan menyimpang dari kesederhanaan cron, saya akan menggunakan launchd, yang akan membutuhkan lebih sedikit pekerjaan daripada contoh Anda untuk systemd.
birch
6

Jika Anda tidak keberatan menggunakan sesuatu selain cronjobs, saya akan menyarankan utilitas yang kurang dikenal at. Cukup tulis skrip pembungkus yang kedua jadwal itu sendiri akan dijalankan dalam 25 jam, dan kemudian panggil program Anda. Ini tampaknya menjadi solusi terbersih.
Misalnya, Anda bisa menulis ini di ~ / script.sh:

echo "bash ~/script.sh" | at now + 25 hours
/usr/bin/yourprogram

Dan kemudian jalankan bash ~/script.shsekali saja.

Terima kasih kepada @HalosGhost untuk ide penjadwalan pekerjaan sekali dalam 25 jam.

Giulio Muscarello
sumber
2
Ada 2 masalah dengan menggunakan atuntuk tujuan ini: (1) jika pekerjaan gagal dieksekusi dengan benar bahkan sekali, itu juga mungkin gagal menjadwal ulang sendiri dan kemudian bukan hanya eksekusi berikutnya tetapi semua eksekusi di masa depan secara efektif dibatalkan sampai pemberitahuan manusia, dan (2) karena pekerjaan itu membutuhkan waktu non-nol untuk berjalan, menggunakan naif now + 25 hoursberarti akan berjalan beberapa detik (atau lebih) setiap kali, dan jika kelambatan ini akan menumpuk dari waktu ke waktu, membuatnya akhirnya berjalan di sepenuhnya salah waktu.
Celada
2
Anda benar tentang # 1; Saya tidak begitu yakin tentang # 2. Meskipun saya tidak memiliki data, saya tidak berpikir penundaan antara jam berubah dan di tempat kerja dipicu dan kemudian dijadwal ulang cukup besar untuk terlihat - terutama mengingat bahwa resolusi atterbatas pada menit dan memulai semua pekerjaan pada [waktu]: 00.
Giulio Muscarello
1
@Celada: Penjadwalan berikutnya atpekerjaan hal pertama di script menghindari masalah tersebut. Sekalipun demikian, jika kesalahan terjadi maka seluruh rantai terputus, yang mungkin atau mungkin tidak diinginkan: jika use case "hanya jalankan ini ketika berfungsi", tidak memulai kembali adalah fitur yang hebat. Tetapi jika use case adalah "selalu berjalan, bahkan jika yang terakhir gagal" maka atbukan alat yang tepat.
Uskup