web-dev-qa-db-ja.com

毎月の最終日をスケジュールします

月の最終日にスクリプトをスケジュールするように指示から読みました。

注:
毎月をカバーするようにdayofmonthの値を設定できないため、毎月の最終日にコマンドを実行するように設定する方法について、読者は疑問に思うかもしれません。この問題はLinuxおよびUnixのプログラマーを悩ませ、かなりの数の異なるソリューションを生み出しました。一般的な方法は、dateコマンドを使用して明日の日付が01かどうかを確認するif-thenステートメントを追加することです。

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

これにより、毎日正午にチェックされ、月の最終日であるかどうかが確認されます。最終日である場合は、cronがコマンドを実行します。

enter image description here

どうやって [`date +%d -d tomorrow` = 01 ]仕事?
状態は正しいですかthen; command1

10
Calculus

概要

正しいコードは次のとおりです。

_#!/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コマンドラインとシェルスクリプティングバイブル」、Christine Bresnahan pp 442、第3版、John Wiley&Sons©2015からの引用です。

はい、それはそれが言うことですが、それは間違っている/不完全です:

  • 終了fiがありません。
  • _[_とそれに続く_`_の間にスペースが必要です。
  • _`…`_の代わりに$(…)を使用することが強く推奨されます。
  • "$(…)"のように、expansionsを引用符で囲むことが重要です。
  • thenの後に追加の_;_があります

どうやって知るの? (まあ、経験によるとbut)しかし、あなたは試すことができます Shellcheck 。本のコードを(アスタリスクの後に)貼り付けると、上記のエラーと「シバンがありません」が表示されます。 Shellcheckでエラーのないスクリプトは次のとおりです。

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

書かれたのは「シェルコード」なので、そのサイトは機能します。これは多くのシェルで機能する構文です。

Shellcheckが触れていないいくつかの問題は次のとおりです。

  • 日付コマンドがGNU日付バージョンです。tomorrowを値として受け入れる_-d_オプションを持つものです(busyboxには-dオプションがあります)しかし、明日は理解できず、BSDには_-d_オプションがありますが、時間の「表示」とは関係ありません)。

  • すべてのオプション_date -d tomorrow +'%d'_をafter形式で設定することをお勧めします。

  • Cronの開始時刻は常に現地時間です。これにより、DST(夏時間)が設定または設定解除された場合、1つのジョブが正確な日数よりも1時間早く開始されることがあります。

完了したのは、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日にのみ実行します。月の終わりを確認する必要はありません。他の日。

正しいパスが含まれていることを確認してください。 cron内のPATHは、ユーザーのPATHと同じになることは(おそらくない)。

one月末スクリプトtestedがあることに注意してください(以下に示します) )他の多くのユーティリティまたはスクリプトを呼び出すことができます。

これにより、cronが完全なコマンドラインで生成する追加の問題も回避されます。

  • Cronは、_%_または_'_(ここでは_"_のみが機能します)で引用されている場合でも、_\_でコマンドラインを分割します。これは、cronジョブが失敗する一般的な方法です。

_end_of_month.sh_スクリプトが特定の日付で正しく機能するかどうかをテストするには、faketimeを使用してテストします(月末に機能しないことを発見せずに)。

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

構文エラーが修正され、コマンドが少し冗長になるように再編成されたと仮定します。

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

これは_date +%d -d tomorrow_(GNU dateが使用されていると仮定)を実行して、明日の日付を2桁の数値として取得します。数値が_01_、次にtodayは月の最終日ではありません。その場合、テストは成功し、_command1_はnotが実行されます。ジョブは実行されます月の最終日になる可能性のある日の正午。

元のコマンド:

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

これにはいくつかの問題があります:

  • _[_の後にスペースがありません。
  • thenの直後の_;_。
  • _%_はcronジョブ仕様で特別であり、_\%_としてエスケープする必要があります(_man 5 crontab_を参照)。
  • 最後にfiと一致する最後のifはありません。
8
Kusalananda