CRON tugas dijalankan pada hari terakhir setiap bulan

95

Saya perlu membuat pekerjaan CRON yang akan dijalankan pada hari terakhir setiap bulan. Saya akan membuatnya menggunakan cPanel.

Bantuan apa pun dihargai. Terima kasih

Utku Dalmaz
sumber

Jawaban:

183

Mungkin cara termudah adalah dengan melakukan tiga pekerjaan terpisah:

55 23 30 4,6,9,11        * myjob.sh
55 23 31 1,3,5,7,8,10,12 * myjob.sh
55 23 28 2               * myjob.sh

Itu akan berjalan pada 28 Februari meskipun, bahkan pada tahun kabisat jadi, jika itu masalah, Anda harus mencari cara lain.


Namun, biasanya jauh lebih mudah dan benar untuk menjalankan pekerjaan secepat mungkin pada hari pertama setiap bulan, dengan sesuatu seperti:

0 0 1 * * myjob.sh

dan memodifikasi skrip untuk memproses data bulan sebelumnya .

Ini menghilangkan semua kerepotan yang mungkin Anda hadapi dengan mencari tahu hari mana yang terakhir bulan itu, dan juga memastikan bahwa semua data untuk bulan itu tersedia, dengan asumsi Anda sedang memproses data. Berlari pada lima menit hingga tengah malam pada hari terakhir setiap bulan mungkin membuat Anda kehilangan apa pun yang terjadi antara saat itu dan tengah malam.

Ini adalah cara yang biasa untuk melakukannya, untuk sebagian besar pekerjaan akhir bulan.


Jika Anda masih benar - benar ingin menjalankannya pada hari terakhir setiap bulan, salah satu opsinya adalah dengan mendeteksi apakah besok adalah yang pertama (baik sebagai bagian dari skrip Anda, atau di crontab itu sendiri).

Jadi, sesuatu seperti:

55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh

seharusnya menjadi awal yang baik, dengan asumsi Anda memiliki program yang relatif cerdas date.

Jika dateprogram Anda tidak cukup maju untuk memberi Anda tanggal relatif, Anda dapat menyusun program yang sangat sederhana untuk memberi Anda hari esok bulan (Anda tidak memerlukan kekuatan penuhdate ), seperti:

#include <stdio.h>
#include <time.h>

int main (void) {
    // Get today, somewhere around midday (no DST issues).

    time_t noonish = time (0);
    struct tm *localtm = localtime (&noonish);
    localtm->tm_hour = 12;

    // Add one day (86,400 seconds).

    noonish = mktime (localtm) + 86400;
    localtm = localtime (&noonish);

    // Output just day of month.

    printf ("%d\n", localtm->tm_mday);

    return 0;
}

lalu gunakan (anggap saja Anda menyebutnya tomdom"hari esok bulan"):

55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh

Meskipun Anda mungkin ingin mempertimbangkan untuk menambahkan pemeriksaan kesalahan karena keduanya time()dan mktime()dapat kembali -1jika terjadi kesalahan. Kode di atas, karena alasan kesederhanaan, tidak memperhitungkannya.

paxdiablo
sumber
7
ya, bisa lebih mudah untuk menjalankan pada setiap hari pertama daripada hari terakhir :)
Utku Dalmaz
1
Memang hari pertama setiap bulan. Beginilah tampilan kode di PHP $ date = new DateTime ('2013-03-01'); $ date-> ubah ('- 1 bulan'); $ bulan sebelumnya = $ tanggal-> format ('Y-m'); // $ beforeMonth sekarang 2013-02. Buat kueri untuk mengambil produk untuk bulan sebelumnya.
Lamy
Data tahun kabisat 29 Februari akan hilang, kita perlu mempertimbangkannya juga. Jawaban Kelinci Guntur di bawah mempertimbangkan itu tetapi cron berjalan dua kali pada bulan Februari tahun kabisat
Hari Swaminathan
1
@Hari, solusi yang lebih disukai adalah menjalankan pada bulan pertama dan mengumpulkan data bulan sebelumnya. 29 Feb tidak akan terlewatkan dalam kasus itu.
paxdiablo
1
Mempertimbangkan tahun kabisat: dan jika Anda tidak keberatan itu berjalan dua kali: 55 23 28,29 2 * myjob.sh
radiantRazor
52

Ada metode yang sedikit lebih pendek yang dapat digunakan mirip dengan salah satu metode di atas. Itu adalah:

[ $(date -d +1day +%d) -eq 1 ] && echo "last day of month"

Juga, entri crontab dapat diperbarui untuk hanya memeriksa tanggal 28 hingga 31 karena tidak ada gunanya menjalankannya di hari-hari lain dalam sebulan. Yang akan memberi Anda:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh
Indie
sumber
Saya tidak bisa membuat ini berfungsi sebagai entri crontab (saya pikir ada sesuatu yang perlu dihindari). Ini bekerja dengan baik dalam skrip shell yang dipanggil dari crontab. FYI, kesalahan yang saya dapatkan adalah /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok
13
Ini bekerja dengan baik. Di file crontab,% harus di-escape. Jadi[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM
Trik yang bagus memang! Tetapi pertanyaannya telah ditandai posixdan tanggal POSIX tidak mendukung "-d +1day": - \ Solusi yang lebih rumit (dan jelek) adalah:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau
15

Siapkan tugas cron untuk dijalankan pada hari pertama setiap bulan. Kemudian ubah jam sistem menjadi satu hari ke depan.

Tom Anderson
sumber
11
yang berarti jam sistem akan selalu salah sepanjang waktu. Maaf, tapi saya pikir ini akan menyebabkan lebih banyak masalah. -1 lalu.
Rudy
70
Sekarang saya tahu bagaimana perasaan Galileo.
Tom Anderson
7
@ iconoclast: Saya ingin menganggapnya lebih sebagai koan daripada lelucon.
Tom Anderson
14
Saya pikir ini adalah ide yang sangat buruk DAN brilian. Jangan lakukan ini di rumah, nak.
Pascal
11
@pascalbetz Oh, orang-orang dapat melakukannya di rumah, tetapi mereka seharusnya tidak melakukannya di tempat kerja.
Tom Anderson
14

Bagaimana dengan yang ini, setelah Wikipedia?

55 23 L * * /full/path/to/command
Piohen
sumber
Nah, bagaimana dengan itu? Itu: "kesalahan hari-bulan yang buruk dalam file crontab, tidak dapat menginstal. Apakah Anda ingin mencoba pengeditan yang sama?"
webjunkie
12
Untuk memperjelas, entri wikipedia itu juga menyebutkan bahwa "L" tidak standar.
sdupton
10

Mengadaptasi solusi paxdiablo, saya menjalankannya pada 28 dan 29 Februari. Data dari tanggal 29 menimpa tanggal 28.

# min  hr  date     month          dow
  55   23  31     1,3,5,7,8,10,12   * /path/monthly_copy_data.sh
  55   23  30     4,6,9,11          * /path/monthly_copy_data.sh
  55   23  28,29  2                 * /path/monthly_copy_data.sh
Kelinci Guntur
sumber
4
Kecuali, tentu saja, pekerjaan tersebut memiliki beberapa aspek yang merusak seperti membersihkan semua data saat diproses :-)
paxdiablo
Ini sebenarnya adalah opsi yang tidak menyakitkan (paling tidak teknis), dan Anda mungkin tidak peduli bahwa tiga kali dalam 4 tahun Anda akan mendapatkan cronjob terlalu dini jika Anda mengabaikan ,29.
Matt
@Matt: Umm, bukankah maksud Anda itu akan berjalan satu hari terlalu dini sekali dalam empat tahun jika entri crontab Anda mengatakan 55   23   28    2?
G-Man Says 'Reinstate Monica'
@ G-Man Ya, Anda benar, dan di atas itu, Anda menjalankannya untuk kedua kalinya pada tanggal 29.
Matt
8

Anda dapat menyiapkan tugas cron untuk dijalankan setiap hari dalam sebulan, dan menjalankannya skrip shell seperti berikut. Skrip ini menentukan apakah nomor hari esok kurang dari hari ini (yaitu jika besok adalah bulan baru), dan kemudian melakukan apapun yang Anda inginkan.

TODAY=`date +%d`
TOMORROW=`date +%d -d "1 day"`

# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
echo "This is the last day of the month"
# Do stuff...
fi
thomson_matt
sumber
Saya tidak yakin apa yang Anda peroleh dengan memeriksa apakah itu kurang dari hari ini - Anda seharusnya dapat memeriksa apakah itu yang pertama. Kecuali jika hari pertama bulan bisa menjadi tanggal 2 atau 3 :-)
paxdiablo
7

Untuk metode yang lebih aman dalam crontab berdasarkan solusi @Indie (gunakan jalur absolut ke date+ $()tidak berfungsi pada semua sistem crontab):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh
zigarn
sumber
5
#########################################################
# Memory Aid 
# environment    HOME=$HOME SHELL=$SHELL LOGNAME=$LOGNAME PATH=$PATH
#########################################################
#
# string         meaning
# ------         -------
# @reboot        Run once, at startup.
# @yearly        Run once a year, "0 0 1 1 *".
# @annually      (same as @yearly)
# @monthly       Run once a month, "0 0 1 * *".
# @weekly        Run once a week, "0 0 * * 0".
# @daily         Run once a day, "0 0 * * *".
# @midnight      (same as @daily)
# @hourly        Run once an hour, "0 * * * *".
#mm     hh      Mday    Mon     Dow     CMD # minute, hour, month-day month DayofW CMD
#........................................Minute of the hour
#|      .................................Hour in the day (0..23)
#|      |       .........................Day of month, 1..31 (mon,tue,wed)
#|      |       |       .................Month (1.12) Jan, Feb.. Dec
#|      |       |       |        ........day of the week 0-6  7==0
#|      |       |       |        |      |command to be executed
#V      V       V       V        V      V
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "Tomorrow is the first today now is  `date`" >> ~/message
1       0       1       *       *       rm -f ~/message
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "HOME=$HOME LOGNAME=$LOGNAME SHELL = $SHELL PATH=$PATH" 
Leslie Satenstein
sumber
5

Untuk implementasi cron AWS Cloudwatch (Penjadwalan Lambdas, dll ..) ini berfungsi:

55 23 L * ? *

Tayang pukul 23:55 pada hari terakhir setiap bulan.

Dan Herman
sumber
4
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/{ print $NF }' | tail -1) ]] && job

Lihat pertanyaan terkait di forum unix.com.

membersihkan
sumber
3

Anda bisa menghubungkan semua jawaban dalam satu baris cron dan hanya menggunakan dateperintah.

Periksa saja perbedaan antara hari dalam sebulan yaitu hari ini dan besok:

0 23 * * * root [ $(expr $(date +\%d -d '1 days') - $(date +\%d)  ) -le 0 ]  && echo true

Jika selisihnya di bawah 0 berarti kita mengganti bulan dan ada hari terakhir bulan tersebut.

Lukasz Stelmach
sumber
3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 
Donald Bebek
sumber
1
menggemakan perintah dan menyalurkannya ke bash adalah cara terbaik saat bekerja dengan cron. Karena cron menggunakan sh dan bukan bash. Periksa di mana bash Anda menggunakan 'bash yang mana'. Di FreeBSD ini adalah / usr / local / bin / bash, di Linux / bin / bash.
Donald Duck
2

Bagaimana dengan ini?

edit .bashprofilepenambahan pengguna :

export LAST_DAY_OF_MONTH=$(cal | awk '!/^$/{ print $NF }' | tail -1)

Kemudian tambahkan entri ini ke crontab:

mm hh * * 1-7 [[ $(date +'%d') -eq $LAST_DAY_OF_MONTH ]] && /absolutepath/myscript.sh
Raul Baron
sumber
0

Hari terakhir setiap bulan bisa 28-31 tergantung pada bulan apa (Feb, Maret dll). Namun dalam salah satu kasus ini, hari berikutnya selalu tanggal 1 bulan depan. Jadi kita dapat menggunakannya untuk memastikan kita menjalankan beberapa pekerjaan selalu pada hari terakhir dalam sebulan menggunakan kode di bawah ini:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
Rakesh Chintha
sumber