Розкладіть останній день кожного місяця


10

Я читаю з інструкції щодо планування сценарію в останній день місяця:

Примітка
. Чутливий читач, можливо, задається питанням, яким чином ви зможете встановити команду для виконання в останній день кожного місяця, оскільки ви не можете встановити значення денного місяця для покриття кожного місяця. Ця проблема наштовхнулася на програмістів Linux і Unix і породила досить багато різних рішень. Поширений метод - додати оператор if-then, який використовує команду date, щоб перевірити, чи завтрашня дата 01:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

Це перевіряється щодня о 12 годині дня, щоб побачити, чи це останній день місяця, і якщо так, то cron виконує команду.

введіть тут опис зображення

Як [`date +%d -d tomorrow` = 01 ]працює?
Чи правильно стверджувати then; command1?


Ви впевнені, що це дослівно, що це говорить? Як написано тут, насправді це не працює.
Майкл Гомер

Я опублікував знімок. @ MichaelHomer
Calculus

Дякую! Я зіткнувся з форматуванням, щоб він збігався - це все ще не зовсім правильно, але це те, що говорить картина.
Майкл Гомер

1
Відсутній ; endif?
danblack

Це не працює. Він містить синтаксичні помилки: немає місця після [і немає fiв кінці. Крім того, %є особливим у кронабі.
Кусалаланда

Відповіді:


17

Анотація

Правильний код повинен бути:

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Викликати цей скрипт end_of_month.shі дзвінок у cron просто:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Це запустить сценарій end_of_month(який внутрішньо перевірятиме, що день є останнім днем ​​місяця) лише в дні 28, 29, 30 та 31. Немає необхідності перевіряти кінець місяця в будь-який інший день.

Старий пост.

Це цитата з книги "Біблійна сценарна команда Linux та сценарій оболонок оболонки" Річарда Блума, Крістін Бреснахан, стр. 442, Третє видання, John Wiley & Sons © 2015.

Так, це сказано, але це неправильно / неповно:

  • Відсутнє закриття fi.
  • Потрібен простір між [та наступним `.
  • Він настійно рекомендується використовувати $ (...) замість `…`.
  • Важливо, що ви використовуєте цитати навколо таких розширень"$(…)"
  • Є додаткове ;післяthen

Звідки я знаю? (ну, за досвідом ☺), але ви можете спробувати Shellcheck . Вставте код із книги (після зірочок), і він покаже вам перераховані вище помилки плюс "пропущений шебанг". Сценарій без помилок у Shellcheck такий:

#!/bin/sh if [ "$(date +%d -d tomorrow)" = 01 ] ; then script.sh; fi

Цей сайт працює, тому що те, що було написано - "код оболонки". Це синтаксис, який працює у багатьох оболонках.

Деякі проблеми, які шелчек не згадує, є:

  • Передбачається, що команда дата є версією дати GNU. Той, у якому є -dпараметр, який приймає tomorrowяк значення (busybox має опцію -d, але не розуміє завтра, а BSD має -dваріант, але не пов'язаний з "відображенням" часу).

  • Краще встановити формат після всіх варіантів date -d tomorrow +'%d'.

  • Час запуску крона завжди знаходиться за місцевим часом, що може зробити одне завдання на 1 годину раніше, ніж точний підрахунок дня, якщо встановлений або відключений режим літнього часу (DST).

Що ми зробили, це сценарій оболонки, який можна було б назвати cron. Ми можемо додатково модифікувати скрипт, щоб прийняти аргументи програми або команди для виконання, як це (нарешті, правильний код):

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Викликати цей скрипт end_of_month.shі дзвінок у cron просто:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Це запустить сценарій end_of_month(який внутрішньо перевірятиме, що день є останнім днем ​​місяця) лише в дні 28, 29, 30 та 31. Немає необхідності перевіряти кінець місяця в будь-який інший день.

Переконайтеся, що введено правильний шлях. PATH всередині cron не буде (неправдоподібно) таким самим, як PATH користувача.

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

Це також дозволить уникнути додаткової проблеми, яку генерує cron за допомогою повного командного рядка:

  • Cron розбиває командний рядок на будь-який, %навіть якщо його цитують 'або "( \тут працює лише твір). Це звичайний спосіб, коли крон не працює.

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

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....

ast-open date(або dateвбудований ksh93, якщо ksh93 був побудований як частина ast-open) робить підтримку date -d tomorrow +%sабо date +%s tomorrow).
Стефан Шазелас

2
Досвід довів (багато разів), що набагато краще перевірити правильність роботи сценарію з факетом, ніж почекати до кінця місяця, щоб встановити, що запланована робота не працює. Як і виявити цю * * * * * echo "$(date -u +'date %c')" >>~/testfileроботу, тому що забути процитувати \%(що стає важко налагодити, якщо можлива лише одна спроба щомісяця). @Kusalananda
Ісаак

8

Якщо припустити, що синтаксичні помилки виправлені, а команда трохи переформульована, щоб бути менш багатослівною:

00 12 28-31 * * [ "$( date -d tomorrow +\%d )" != "01" ] || command1

Це запускається date +%d -d tomorrow(якщо припустити, що використовується GNU date), щоб отримати завтрашню дату як двоцифрове число. Якщо це число не 01, то сьогодні це не останній день місяця. У цьому випадку випробування успішно , і command1це НЕ виконується. Робота виконується опівдні в дні, які, можливо, можуть бути останнім днем ​​місяця.

Оригінальна команда:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

У цьому є кілька питань:

  • Немає місця після [.
  • А ;безпосередньо після then.
  • %є особливим у специфікаціях роботи cron, і його потрібно уникати як \%(див. man 5 crontab).
  • В fiкінці немає жодного фіналу, який би збігався з if.

2
Проблема з використанням [ ... ] && command1замість цього if...полягає в тому, що в дні, які не є останнім днем ​​місяця, робота cron закінчиться ненульовим статусом виходу, і про цю помилку, можливо, доведеться повідомити. Використання [ "$(...)" != 01 ] || command1- це ще один спосіб уникнути проблеми.
Стефан Шазелас

1
Ще одна проблема з оригінальним кодом - ;між thenі command1.
Стефан Шазелас

@ StéphaneChazelas Ще одна причина, чому мені не подобаються однолінійки, їх важко читати.
Кусалаланда

Різниця в часі між послідовними запусками команди може бути не цілою кількістю (24 години) днів, оскільки зміна DST змістить час початку cron.
Ісаак

3
@cat Не має значення, як ви цитуєте, "або '(за винятком \), знак відсотка %змусить крон перервати лінію на дві частини. Це один звичайний спосіб збити крон на провал.
Ісаак

-1

Для того, щоб запланувати в останній день кожного місяця, ви можете спробувати: 0 0 15,L * *.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.