3日ごとに文字通りbashスクリプトを実行する


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日が経過していない場合」:何日から3日?質問を編集して明確にしてください。
terdon 2016

4
文字通り文字通り?これはx / yの質問のようで、実際に何をしようとしているのかを話したいと思うかもしれません。crontabによるスクリプトの実行を停止しようとしていますか?
ジャーニーマンオタク2016

1
質問を編集して、crontabソリューションが機能しない理由を説明しました。
Taavi

以下のようなものがdate("z") % 3 == 0同様の問題に苦しむでしょう:12月はうるう年の一部であったことをしない限り、条件は、12月29日と1月3日の間に4日間falseになります。
Rhymoid 2016

回答:


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日です)。


はい、特定のプログラムの最後に成功した呼び出しを記録するには、ある種の外部データストアが必要であり、ファイルシステムはそのための明らかな選択肢です。
Kilian Foth 2016

日付を保存する必要さえありません-毎回ファイルをタッチして、タイムスタンプを確認してください。
djsmiley2kStaysIn '

@ djsmiley2kはい、そうです。スクリプトファイルを手動で変更すると遅延がリセットされるリスクが許容できる場合は、変更タイムスタンプを使用することもできます。私はそれを私の答えに加えました。
バイトコマンダー

これは、現在のタイムスタンプを(人間が読める日付の代わりに)ファイルに保存することでわずかに調整できるため、で経過時間を取得できます$[ $(date +%s) - $(< lastrun) ]。スクリプトがcronから1日に1回実行される場合、必要な時間間隔に多少の余裕を持たせる可能性があります。これにより、スクリプトの実行が数秒遅れても、次回は丸一日スキップされません。71時間がosltを経過した場合、それは毎日チェックされます。
ilkkachu

datefileを使用したこの魔法使いは実際に機能しました。どうもありがとうございました。
Taavi 2016

12

Cronは実際にはこのための間違ったツールです。実際に機能する可能性のあるatと呼ばれる一般的に使用され、あまり使用されいないツールがあります。atはにインタラクティブな使用のため設計されており、これを行うためのより良い方法を誰かが見つけると確信しています。

私の場合、実行しているスクリプトをtestjobs.txtにリストし、次の行を含めます。

例として、私はこれをtestjobs.txtとして持っています

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

私はあなたのシェルスクリプトかもしれない2つの無実のコマンドを持っています。echoを実行して確定的な出力があることを確認し、必要に応じてコマンドの実行を確認する日付を指定します。atがこのコマンドを実行すると、新しいジョブをatに追加して3日間終了します。(私は1分でテストしました-うまくいきます)

私はかなり確信して、私は私が虐待されてきた方法のために呼び出されるでしょうけど、実行するコマンドスケジューリングするための便利なツールで、前のコマンドの後に、時間やX日を。


3
ここでは+1 atの方が適切と思われます。このアプローチの問題は、スクリプトを手動で実行するたびに、3日ごとのatタスクの別のセットを追加することです。
Dewi Morgan

1
+1ですが、(@ DewiMorganが指すものに加えて)別の問題は、1つのスクリプトが失敗した場合、スクリプトが失敗したことに気づくまで、後続のすべてのスクリプトが起動されないことです(atが失敗ポイントの後にある場合)。失敗して再起動しました。これは悪い場合もあれば、良い場合もあります(例:条件がなくなったために失敗する:3日以内に再試行しないのは良いですか?)。また、毎回わずかなドリフトがあります(at上部にある場合は数ミリ秒at、長い実行スクリプトの下部にある場合はさらに長くなる可能性があります)
Olivier Dulac '26 / 09/26

でメールを送信できます...失敗の解決策となる可能性があります。atqとatrmを使用すると、誤ったジョブを選択できますか?理論的には、atの特定の日付をスクリプト化することができますが、それはエレガントで手に負えないようです。
ジャーニーマンオタク2016

したがって、atの次の実行の存在をチェックするcronjobがあります。ない場合は、3日間追加して警告します(またはすぐに実行して、もう一度確認します)。
djsmiley2kStaysInside '26

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

このループは常に真であるため、常にコードを実行し、3日間待ってからループを再開します。


なんでwhile true
ジョナサンレフラー

ハァッ!良いキャッチ。私はそれを長い間使用する必要がなかった、それが存在していたのを忘れていたlol
mkingsbu

2
at解決策として、これは各実行のスクリプトの実行時間によってドリフトします。もちろん、スクリプトを開始してスリープする時間を3日後まで節約することで回避できます。
ilkkachu 2016

2

cronの代わりにanacronを使用できます。これは、必要なことを正確に実行するように設計されています。マンページから:

Anacronは、日数で指定された頻度で、定期的にコマンドを実行するために使用できます。cron(8)とは異なり、マシンが継続的に実行されているとは限りません。したがって、1日24時間稼働していないマシンで使用して、通常はcronで制御される毎日、毎週、毎月のジョブを制御できます。

実行されると、Anacronは構成ファイル、通常は/ etc / anacrontab(anacrontab(5)を参照)からジョブのリストを読み取ります。このファイルには、Anacronが制御するジョブのリストが含まれています。各ジョブエントリは、日数の期間、分単位の遅延、一意のジョブ識別子、およびシェルコマンドを指定します。

Anacronは、ジョブごとに、このジョブが過去n日間に実行されたかどうかを確認します。nは、そのジョブに指定された期間です。そうでない場合、Anacronは、遅延パラメーターとして指定された分数だけ待機した後、ジョブのシェルコマンドを実行します。

コマンドが終了すると、Anacronはそのジョブの特別なタイムスタンプファイルに日付を記録するため、いつ再実行するかを知ることができます。時間の計算には日付のみが使用されます。時間は使用されません。


いいでしょう、私はアナクロンをテストします。なぜそれがそのような魔法をかけることができるのにそれほど過小評価されているのか疑問に思う
Taavi

本当の質問:なぜ今ではcronがより良いものに置き換えられていないのですか?fcronが存在し、systemdが独自のソリューションに取り組んでいると思いますが、現在の状況については最新ではありません。
Twinkles 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.