Jalankan skrip bash secara harfiah setelah setiap 3 hari

8

Saya ingin menjalankan skrip shell secara harfiah setelah setiap 3 hari. Menggunakan crontab dengan 01 00 */3 * *benar-benar tidak akan memenuhi syarat, karena akan berjalan pada tanggal 31 dan sekali lagi pada hari pertama dalam sebulan. The */3sintaks adalah sama dengan mengatakan 1,4,7 ... 25,28,31.

Seharusnya ada cara untuk membuat skrip itu sendiri memeriksa kondisi dan keluar jika 3 hari belum berlalu. Jadi crontab mengeksekusi skrip setiap hari, tetapi skrip itu sendiri akan memeriksa apakah 3 hari telah berlalu.

Saya bahkan menemukan beberapa kode, tetapi itu memberi saya kesalahan sintaks, bantuan apa pun akan dihargai.

if (! (date("z") % 3)) {
     exit;
}

main.sh: line 1: syntax error near unexpected token `"z"'
main.sh: line 1: `if (! (date("z") % 3)) {'
Taavi
sumber
4
Apa maksudmu sebenarnya? Bagaimana */3tidak bekerja "jika 3 hari belum berlalu": ​​tiga hari sejak apa? Harap edit pertanyaan Anda dan klarifikasi.
terdon
4
secara harfiah? Ini sepertinya pertanyaan x / y dan Anda mungkin ingin benar-benar berbicara tentang apa yang Anda coba lakukan. Apakah Anda mencoba menghentikan crontab dari menjalankan skrip?
Journeyman Geek
1
Mengedit pertanyaan untuk menjelaskan mengapa solusi crontab tidak berfungsi.
Taavi
Sesuatu seperti date("z") % 3 == 0akan menderita masalah yang sama: kondisinya akan salah selama empat hari antara 29 Desember dan 3 Januari, kecuali jika Desember itu adalah bagian dari tahun kabisat.
Rhymoid

Jawaban:

12

Untuk segera membatalkan dan keluar dari skrip jika eksekusi terakhir belum setidaknya pada waktu yang lalu, Anda dapat menggunakan metode ini yang memerlukan file eksternal yang menyimpan tanggal dan waktu eksekusi terakhir.

Tambahkan baris ini ke bagian atas skrip Bash Anda:

#!/bin/bash

# File that stores the last execution date in plain text:
datefile=/path/to/your/datefile

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Test if datefile exists and compare the difference between the stored date 
# and now with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test -f "$datefile" ; then
    if test "$(($(date "+%s")-$(date -f "$datefile" "+%s")))" -lt "$seconds" ; then
        echo "This script may not yet be started again."
        exit 1
    fi
fi

# Store the current date and time in datefile
date -R > "$datefile"

# Insert your normal script here:

Jangan lupa untuk menetapkan nilai yang berarti datefile=dan sesuaikan nilai seconds=dengan kebutuhan Anda ( $((60*60*24*3))dievaluasi hingga 3 hari).


Jika Anda tidak ingin file terpisah, Anda juga bisa menyimpan waktu eksekusi terakhir di cap waktu modifikasi skrip Anda. Namun itu berarti bahwa membuat perubahan apa pun pada file skrip Anda akan mengatur ulang penghitung 3 dan diperlakukan seperti jika skrip berhasil dijalankan.

Untuk menerapkannya, tambahkan snippet di bawah ini ke bagian atas file skrip Anda:

#!/bin/bash

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Compare the difference between this script's modification time stamp 
# and the current date with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test "$(($(date "+%s")-$(date -r "$0" "+%s")))" -lt "$seconds" ; then
    echo "This script may not yet be started again."
    exit 1
fi

# Store the current date as modification time stamp of this script file
touch -m -- "$0"

# Insert your normal script here:

Sekali lagi, jangan lupa untuk menyesuaikan nilai seconds=dengan kebutuhan Anda ( $((60*60*24*3))dievaluasi hingga 3 hari).

Komandan Byte
sumber
Ya, merekam permohonan sukses terakhir dari suatu program tertentu membutuhkan semacam penyimpanan data eksternal, dan sistem file adalah pilihan yang jelas untuk itu.
Kilian Foth
Bahkan tidak perlu menyimpan tanggal - cukup sentuh file setiap kali dan periksa stempel waktunya.
djsmiley2kStaysInside
@ djsmiley2k Ya, Anda benar. Jika risiko bahwa memodifikasi file skrip secara manual menyebabkan keterlambatan untuk mengatur ulang dapat diterima, seseorang juga dapat menggunakan cap waktu modifikasi. Saya menambahkan itu ke jawaban saya.
Byte Commander
Ini dapat di-golf sedikit dengan menyimpan stempel waktu saat ini ke file (bukan tanggal yang dapat dibaca manusia), sehingga Anda dapat memperoleh waktu yang telah berlalu bersama $[ $(date +%s) - $(< lastrun) ]. Jika skrip dijalankan dari cron sekali sehari, saya mungkin menambahkan beberapa slack ke interval waktu yang diperlukan, sehingga jika eksekusi skrip tertunda beberapa detik, waktu berikutnya tidak akan melewati satu hari penuh. Yaitu memeriksa setiap hari jika 71 jam telah melewati oslt.
ilkkachu
Sihir dengan datefile ini benar-benar bekerja, terima kasih banyak!
Taavi
12

Cron benar-benar alat yang salah untuk ini. Sebenarnya ada alat yang biasa digunakan dan kurang diperhatikan yang disebut di mana mungkin bekerja. di terutama dirancang untuk penggunaan interaktif dan saya yakin seseorang akan menemukan cara yang lebih baik untuk melakukan ini.

Dalam kasus saya, saya akan memiliki skrip yang saya jalankan terdaftar di testjobs.txt, dan termasuk baris yang berbunyi.

Sebagai contoh, saya akan menggunakan ini sebagai testjobs.txt

echo "cat" >> foo.txt
date >> foo.txt
at now + 3 days < testjobs.txt

Saya memiliki dua perintah yang tidak bersalah, yang mungkin merupakan skrip shell Anda. Saya menjalankan echo untuk memastikan saya memiliki keluaran deterministik, dan tanggal untuk mengonfirmasi perintah berjalan sesuai kebutuhan. Ketika di jalankan perintah ini, itu akan selesai dengan menambahkan pekerjaan baru ke selama 3 hari. (Saya diuji dengan satu menit - yang bekerja)

Saya cukup yakin saya akan dipanggil untuk cara saya dilecehkan, tetapi itu alat yang berguna untuk menjadwalkan perintah untuk dijalankan pada suatu waktu atau x hari setelah perintah sebelumnya.

Journeyman Geek
sumber
3
+1 atsepertinya pendekatan yang lebih baik di sini. Masalah dengan pendekatan ini adalah bahwa setiap kali Anda menjalankan skrip secara manual, Anda menambahkan serangkaian attugas setiap 3 hari .
Dewi Morgan
1
+1, tetapi masalah lain (selain yang ditunjukkan oleh @DewiMorgan) adalah bahwa jika satu skrip gagal, semua skrip berikutnya tidak akan diluncurkan (jika at adalah setelah titik kegagalan), sampai seseorang menyadari bahwa skrip tersebut telah gagal dan meluncurkannya kembali. Ini bisa buruk, atau kadang-kadang baik (mis: gagal karena kondisinya tidak ada lagi: ada baiknya tidak coba lagi dalam 3 hari?). Dan ada sedikit pergeseran setiap waktu (beberapa milidetik jika atada di atas, atau berpotensi jauh lebih banyak jika atberada di dekat bagian bawah skrip eksekusi panjang)
Olivier Dulac
Di dapat mengirim email .... Yang mungkin bisa menjadi solusi untuk kegagalan. atq dan atrm akan memungkinkan untuk pemusnahan pekerjaan yang salah mungkin? Secara teori Anda bisa menuliskan tanggal tertentu untuk tetapi itu tampaknya tidak berlaku dan pegangan.
Journeyman Geek
Jadi memiliki tugas cronjob yang memeriksa keberadaan run berikutnya di; jika tidak ada, tambahkan satu selama 3 hari dan peringatkan (atau jalankan segera, dan periksa lagi).
djsmiley2kStaysInside
3

Pertama, fragmen kode di atas adalah sintaks Bash tidak valid, seperti Perl. Kedua, zparameter yang datemenyebabkannya menampilkan zona waktu numerik. +%jadalah nomor hari. Anda membutuhkan:

if [[ ! $(( $(date +%j) % 3 )) ]] ;then
     exit
fi

Tapi Anda masih akan melihat keanehan di akhir tahun:

$ for i in 364 365  1 ; do echo "Day $i, $(( $i % 3 ))"; done
Day 364, 1
Day 365, 2
Day 1, 1

Anda mungkin lebih beruntung dengan menyimpan hitungan dalam file, dan menguji / memperbarui itu.

waltinator
sumber
1
Perl tidak memiliki date()fungsi bawaan, tetapi itu terlihat seperti date () di PHP (di mana z"Hari dalam setahun (mulai dari 0)")
ilkkachu
2

Jika Anda dapat membiarkan skrip berjalan terus-menerus, Anda dapat melakukannya:

while true; do

[inert code here]

sleep 259200
done

Loop ini selalu benar, sehingga selalu menjalankan kode, lalu menunggu selama tiga hari sebelum memulai loop lagi.

mkingsbu
sumber
Mengapa tidak while true?
Jonathan Leffler
Hah! Tangkapan yang bagus. Saya tidak perlu menggunakannya dalam waktu yang lama, saya lupa itu ada lol
mkingsbu
2
Sebagai atsolusinya, ini akan melayang dengan waktu eksekusi skrip setiap kali dijalankan. Tentu saja itu dapat diatasi dengan menghemat waktu ketika skrip dimulai dan tidur hingga 3 hari dari itu.
ilkkachu
2

Anda dapat menggunakan anacron alih-alih cron, ia dirancang persis untuk melakukan apa yang Anda butuhkan. Dari halaman manual:

Anacron dapat digunakan untuk menjalankan perintah secara berkala, dengan frekuensi yang ditentukan dalam beberapa hari. Tidak seperti cron (8), itu tidak mengasumsikan bahwa mesin berjalan terus menerus. Oleh karena itu, dapat digunakan pada mesin yang tidak beroperasi 24 jam sehari, untuk mengontrol pekerjaan harian, mingguan, dan bulanan yang biasanya dikendalikan oleh cron.

Ketika dijalankan, Anacron membaca daftar pekerjaan dari file konfigurasi, biasanya / etc / anacrontab (lihat anacrontab (5)). File ini berisi daftar pekerjaan yang dikendalikan Anacron. Setiap entri pekerjaan menentukan periode dalam hari, penundaan dalam menit, pengidentifikasi pekerjaan yang unik, dan perintah shell.

Untuk setiap pekerjaan, Anacron memeriksa apakah pekerjaan ini telah dieksekusi dalam n hari terakhir, di mana n adalah periode yang ditentukan untuk pekerjaan itu. Jika tidak, Anacron menjalankan perintah shell job, setelah menunggu jumlah menit yang ditentukan sebagai parameter penundaan.

Setelah perintah keluar, Anacron mencatat tanggal dalam file stempel waktu khusus untuk pekerjaan itu, sehingga dapat mengetahui kapan harus menjalankannya lagi. Hanya tanggal yang digunakan untuk perhitungan waktu. Jamnya tidak digunakan.

Twinkles
sumber
Bagus, saya pasti akan menguji Anacron. Bertanya-tanya mengapa itu sangat diremehkan jika bisa melakukan sihir seperti itu
Taavi
Pertanyaan sebenarnya: mengapa cron belum diganti dengan sesuatu yang lebih baik sekarang? fcron ada dan saya pikir systemd bekerja pada solusinya sendiri, tapi saya tidak up to date dengan keadaan saat ini.
Twinkles