Выполнять скрипт 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
буквально буквально? Это похоже на х / у вопрос, и вы можете поговорить о том, что вы пытаетесь сделать. Вы пытаетесь остановить запуск скрипта 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 Да, ты прав. Если риск того, что изменение файла сценария вручную приведет к задержке сброса, является приемлемым, можно также использовать метку времени изменения. Я добавил это к своему ответу.
— Byte Commander

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

Это волшебство с datefile на самом деле сработало, большое спасибо!
— Таави

12

Cron действительно не тот инструмент для этого. Там на самом деле широко используются и underloved инструмента под названием в каком могут работать. Он в основном предназначен для интерактивного использования, и я уверен, что кто-то найдет лучший способ сделать это.

В моем случае я бы запустил скрипт, который я запускаю, в testjobs.txt и включил в себя строку, которая гласит:

В качестве примера я хотел бы иметь это как testjobs.txt

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

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

Я уверен , что я буду окликнул за то , как я злоупотреблял в, но удобный инструмент для планирования команды , которая будет работать в то время , или х дни после предыдущей команды.


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

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()встроенной функции, но это немного похоже на date () в PHP (где z«день года (начиная с 0)»)
— ilkkachu

2

Если вы можете просто оставить скрипт запущенным постоянно, вы можете сделать:

while true; do

[inert code here]

sleep 259200
done

Этот цикл всегда имеет значение true, поэтому он всегда будет выполнять код, затем подождать три дня, прежде чем снова запустить цикл.


Почему нет while true?
— Джонатан Леффлер

Хах! Хороший улов. Я не нуждался в том, чтобы использовать это в течение долгого времени, я забыл, что это существовало LOL
— Mkingsbu

2
Как atрешение, это будет дрейфовать временем выполнения сценария при каждом запуске. Конечно, это можно обойти, сэкономив время, когда скрипт запускается и спит до 3 дней.
— ilkkachu

2

Вы можете использовать анакрон вместо cron, он предназначен именно для того, что вам нужно. Из справочной страницы:

Anacron может использоваться для периодического выполнения команд с частотой, указанной в днях. В отличие от cron (8), он не предполагает, что машина работает непрерывно. Следовательно, его можно использовать на машинах, которые не работают 24 часа в сутки, для контроля ежедневных, еженедельных и ежемесячных заданий, которые обычно контролируются cron.

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

Для каждого задания Anacron проверяет, было ли это задание выполнено за последние n дней, где n - период, указанный для этого задания. Если нет, Anacron запускает команду оболочки задания, ожидая количество минут, указанное в качестве параметра задержки.

После выхода из команды Anacron записывает дату в специальный файл метки времени для этого задания, чтобы он мог знать, когда его выполнить снова. Только дата используется для расчета времени. Час не используется.


Хорошо, я обязательно проверю Anacron. Интересно, почему это так недооценивают, если он может творить такую ​​магию
— Таави

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