Виконуйте скрипт bash буквально через кожні 3 дні


8

Я хочу виконувати скрипт оболонки буквально через кожні 3 дні. Використання crontab з 01 00 */3 * *фактично не виконає цю умову, оскільки воно буде працювати 31-го, а потім 1-го числа місяця. */3Синтаксис такий же , як кажуть 1,4,7 ... 25,28,31.

Повинні бути способи змусити сам скрипт перевірити умови та вийти, якщо 3 дні не минуло. Отже, crontab виконує сценарій щодня, але сам сценарій перевірить, чи минуло 3 дні.

Я навіть знайшов якийсь код, але він дав мені синтаксичну помилку, будь-яка допомога буде вдячна.

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

main.sh: line 1: syntax error near unexpected token `"z"'
main.sh: line 1: `if (! (date("z") % 3)) {'

4
Що саме ви маєте на увазі? Як */3не працює? "якщо 3 дні не минули": три дні з того часу? Будь ласка , змініть своє питання і уточнити.
тердон

4
буквально буквально? Це здається питанням x / y, і ви, можливо, хочете насправді поговорити про те, що ви намагаєтеся зробити. Ви намагаєтесь зупинити crontab від запуску сценарію?
Подорож Гек

1
Відредагував питання, щоб пояснити, чому рішення crontab не працюватиме.
Тааві

Щось таке date("z") % 3 == 0страждає від подібної проблеми: умова буде помилковою протягом чотирьох днів між 29 грудня та 3 січня, якщо тільки грудень не був частиною високосного року.
Римоїд

Відповіді:


12

Для негайного переривання та виходу із сценарію, якщо останнє виконання ще не було хоча б певний час тому, ви можете використовувати цей метод, який вимагає зовнішнього файлу, який зберігає останню дату та час виконання.

Додайте ці рядки до верхньої частини сценарію Bash:

#!/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:

Не забудьте встановити змістовне значення datefile=та адаптувати цінність seconds=до ваших потреб ( $((60*60*24*3))оцінюється до 3 днів).


Якщо ви не хочете окремого файлу, ви також можете зберігати останній час виконання у відмітці часу сценарію модифікації. Однак це означає, що внесення будь-яких змін у ваш файл сценарію скидає 3 лічильника і буде оброблятися так, якби сценарій успішно виконувався.

Щоб здійснити це, додайте фрагмент нижче вгорі файлу сценарію:

#!/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:

Знову ж таки, не забудьте адаптувати цінність seconds=до ваших потреб ( $((60*60*24*3))оцінюється до 3 днів).


Так, запис останнього успішного виклику певної програми вимагає певного зовнішнього сховища даних, і файлова система є очевидним вибором для цього.
Кіліан Фот

Навіть не потрібно зберігати дату - просто торкайтеся файлу кожен раз і перевіряйте, чи є його часова марка.
djsmiley2kStaysInside

@ djsmiley2k Так, ви праві. Якщо ризик того, що зміна файлу сценарію вручну спричиняє затримку до скидання, є прийнятним, можна також використовувати часову позначку модифікації. Я додав це до своєї відповіді.
Байт-командир

Це можна злегка переграти, збереживши поточну позначку часу у файл (замість дати для читання людиною), тож ви зможете отримати минулий час $[ $(date +%s) - $(< lastrun) ]. Якщо сценарій запускається з крона один раз на день, я можу додати деяку слабкість до необхідного інтервалу часу, так що якщо виконання сценарію затримається на пару секунд, наступного разу не пропуститься цілий день. Тобто щодня перевіряйте, чи минуло 71 год.
ilkkachu

Цей майстер із файлом дат насправді спрацював, дуже дякую!
Тааві

12

Cron справді є неправильним інструментом для цього. Насправді є загальновживаним і недоотриманим інструментом, на якому може працювати. в основному призначений для інтерактивного використання, і я впевнений, що хтось знайде кращий спосіб зробити це.

У моєму випадку я мав би сценарій, який я запускаю, перелічений у testjobs.txt і включав би рядок, який читається.

Як приклад, я мав би це як testjobs.txt

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

У мене є дві невинні команди, які можуть бути вашими оболонками. Я запускаю ехо, щоб переконатися, що у мене детермінований вихід, і дата, щоб підтвердити виконання команд за потребою. Коли буде запущена ця команда, вона завершиться, додавши нове завдання до в протягом 3 днів. (Я перевірив одну хвилину - яка працює)

Я впевнений, що мене викличуть за спосіб, яким я зловживав, але це зручний інструмент для планування команди, яку потрібно запустити на час або x днів після попередньої команди.


3
+1, як atздається, кращий підхід тут. Проблема такого підходу полягає в тому, що щоразу, коли ви вручну запускаєте сценарій, ви додаєте інший набір завдань кожні 3 дні at.
Деві Морган

1
+1, але інша проблема (крім тієї, на яку вказав @DewiMorgan), полягає в тому, що якщо один сценарій не вдасться, всі наступні сценарії не будуть запускатися (якщо значення at після точки відмови), поки хто не зрозуміє, що сценарій мав не вдалося і відновити його. Це може бути погано, а іноді і добре (напр .: не вдається, тому що стану вже немає: добре, що він не повторюється через 3 дні?). І кожен раз спостерігається невеликий занос (кілька мілісекунд, якщо він atзнаходиться вгорі, або, можливо, набагато більше, якщо atзнаходиться біля нижньої частини сценарію довгого виконання)
Олів'є Дулак,

Можна відправляти електронні листи .... Що може бути вирішенням проблеми. atq і atrm, можливо, дозволить відкинути помилкові роботи, можливо? Теоретично, ви можете прописати конкретну дату для, але це здається неелегантним і ручним.
Подорож Geek

Отже, майте cronjob, який перевіряє наявність наступного запуску в at; якщо такої немає, додайте її на 3 дні та попередите (або запустіть її негайно та перевірте ще раз).
djsmiley2kStaysInside

3

По-перше, наведений вище фрагмент коду - недійсний синтаксис Bash, схожий на Perl. По-друге, zпараметр dateзмушує його виводити числовий часовий пояс. +%j- номер дня. Тобі потрібно:

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

Але ви все одно будете бачити дивацтва наприкінці року:

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

Можливо, вам пощастить із збереженням рахунку у файлі та його тестуванням / оновленням.


1
У Perl немає date()вбудованої функції, але це виглядає дещо як дата () у PHP (де z"День року (починаючи з 0)")
ilkkachu

2

Якщо ви можете просто залишити сценарій постійно працювати, ви можете це зробити:

while true; do

[inert code here]

sleep 259200
done

Цей цикл завжди вірний, тому він завжди буде виконувати код, а потім зачекайте три дні, перш ніж знову запустити цикл.


Чому ні while true?
Джонатан Леффлер

Га! Хороший улов. Мені не потрібно було користуватися цим довгий час, я забув, що він існував lol
mkingsbu

2
Як atрішення, це залежатиме від часу виконання сценарію кожного запуску. Звичайно, це можна вирішити, заощадивши час, коли сценарій запускається і спати до 3 днів від цього.
ilkkachu

2

Можна використовувати анакрон замість крона, він призначений саме для того, щоб робити те, що потрібно. На сторінці сторінки:

Анакрон можна використовувати для виконання команд періодично з періодичністю, вказаною в днях. На відміну від cron (8), він не передбачає, що машина працює постійно. Отже, його можна використовувати на машинах, які не працюють 24 години на добу, для управління роботами щоденно, щотижня та щомісяця, які зазвичай контролюються cron.

При виконанні Anacron зчитує список завдань з файлу конфігурації, як правило, / etc / anacrontab (див. Anacrontab (5)). Цей файл містить перелік завдань, якими керує Anacron. Кожен запис завдання вказує період у днях, затримку хвилин, унікальний ідентифікатор завдання та команду оболонки.

Для кожної роботи Anacron перевіряє, чи виконувалася ця робота за останні п ять днів, де n - період, визначений для цієї роботи. Якщо ні, Anacron виконує команду оболонки завдання, очікуючи кількість хвилин, вказану як параметр затримки.

Після завершення команди Anacron записує дату в спеціальний файл часових позначок для цієї роботи, щоб він міг знати, коли її виконати ще раз. Для розрахунків часу використовується лише дата. Година не використовується.


Добре, я обов'язково випробую Anacron. Цікаво, чому він настільки занижений, якщо він може робити таку магію
Тааві,

Справжнє запитання: чому досі крон не був замінений чимось кращим? fcron існує, і я думаю, що systemd працює над власним рішенням, але я не в курсі сучасного стану справ.
Мерехтіння
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.