web-dev-qa-db-ja.com

2週間ごとにジョブを実行するようにcronに指示する方法は?

Cronを介してジョブを実行します。このジョブは、指定された時刻に毎週火曜日に実行されます。毎週火曜日は簡単です:

0 6 * * Tue

しかし、「2番目の火曜日」(またはお好みであれば-2週間ごと)にどうやって作るのでしょうか?スクリプト自体にロジックを実装したくありませんが、cronでのみ定義を保持します。

55
tkokoszka

これについては、最初の5つのフィールドで正確に定義されていなくても、crontabに保持されます。

0 6 * * Tue expr `date +\%W` \% 2 > /dev/null || /scripts/fortnightly.sh
42
xahtep

回答

火曜日のcronロジックを変更して、1週間おきに実行しますエポック以降

週に604800秒があることを知って(DSTの変更とうるう秒を無視して、ありがとう)、GNU date:

0 6 * * Tue expr `date +\%s` / 604800 \% 2 >/dev/null || /scripts/fortnightly.sh

さておき

カレンダーの計算はイライラします。

@xahtepの答えは素晴らしいですが、 @ Doppelgangerがコメントで指摘 のように、特定の年の境界で失敗します。 date ユーティリティの「年間通算週」指定子はどれもここでは役立ちません。 1月上旬の一部の火曜日は、必然的に前年の最終火曜日の週パリティを繰り返します:2016-01-05(%V)、2018-01-02(%U)、および2019-01-01(%W) 。

42
pilcrow

pilcrow's answer は素晴らしい。ただし、(エポック以降)毎週even週ごとにfortnightly.shスクリプトが実行されます。スクリプトをodd週に実行する必要がある場合は、彼の答えを少し調整できます。

0 6 * * Tue expr \( `date +\%s` / 604800 + 1 \) \% 2 > /dev/null || /scripts/fortnightly.sh

1を0に変更すると、数週間に戻ります。

7
notorious.dds

ちょっと馬鹿げているかもしれませんが、2つのcronjobを作成できます。

最初のcronjob:

0 0 8 ? 1/1 TUE#1 *

2番目のcronjob:

0 0 8 ? 1/1 TUE#3 *

ここの構文についてはわかりませんが、これらを作成するために http://www.cronmaker.com/ を使用しました。

2
Hans

一部のEdgeのケースで失敗する可能性がある上記のアプローチのいくつかの追加の制限を発見しました。たとえば、次のことを考慮してください。

@xahtepと@Doppelgangerは、%W上記の特定の年の境界。

@pilcrowの答えはこれをある程度回避しますが、特定の境界では失敗します。このトピックまたは他の関連トピックの回答では、1日の秒数(対週)を使用しますが、これも同じ理由で特定の境界で失敗します。

これは、これらのアプローチがUTC時間(日付+%s)に依存しているためです。第2火曜日の午前1時と午後10時にジョブを実行している場合を考えます。

GMT-2の場合:

  • 現地時間午前1時= UTC午後11時昨日
  • 現地時間午後10時= UTC午後8時今日

毎日1回だけチェックしている場合、これは問題になりませんが、複数回チェックしている場合、またはUTC時間に近く、夏時間が発生している場合、スクリプトはこれらをチェックしません同日。

これを回避するには、UTCではなくlocalタイムゾーンに基づいてUTCからのオフセットを計算する必要があります。 BASHでこれを行う簡単な方法が見つからなかったため、Perlのクイックワンライナーを使用してUTCからのオフセットを秒単位で計算するソリューションを開発しました。

このスクリプトは、ローカルタイムゾーンを出力する日付+%zを利用します。

Bashスクリプト:

TZ_OFFSET=$( date +%z | Perl -ne '$_ =~ /([+-])(\d{2})(\d{2})/; print eval($1."60**2") * ($2 + $3/60);' )
DAY_PARITY=$(( ( `date +%s` + ${TZ_OFFSET} ) / 86400 % 2 ))

次に、その日が偶数か奇数かを判断します。

if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi
1
dxdc

指定された開始日に基づいてそれを行いたい場合:

0 6 * * 1 expr \( `date +\%s` / 86400 - `date --date='2018-03-19' +\%s` / 86400 \) \% 14 == 0 > /dev/null && /scripts/fortnightly.sh

2018年3月19日から始まる隔週の月曜日に発砲する必要があります

式の読み取り:次の場合、月曜日の午前6時に実行.

1-今日の日付を秒単位で取得し、1日の秒数で割って日数エポックに変換します

2-開始日にも同じことを行い、エポック以降の日数に変換します

3-2つの違いを取得する

4-14で除算して残りを確認する

5-残りがゼロの場合、2週間サイクルです

1
Paul Lemmons

3週間ごとはどうですか?

私の提案は次のとおりです。

0 6 * * Tue expr `date +\%W` \% 3 == 0 > /dev/null || /scripts/fortnightly.sh

...または...

0 6 * * Tue expr `date +\%W` \% 3 == 1 > /dev/null || /scripts/fortnightly.sh

...またはもちろん...

0 6 * * Tue expr `date +\%W` \% 3 == 2 > /dev/null || /scripts/fortnightly.sh

...週の回転に応じて。

0
macetw