特定の時間まで bash スクリプトをスリープさせたい。というわけで、「sleep」のようにインターバルなしで終了時刻を指定し、それまでスリープするコマンドが欲しいです。
特定の日時まで実行中のスクリプトをブロックする必要があるため、「at」デーモンは解決策ではありません。
そのようなコマンドはありますか?
Outlaw Programmer が述べたように、解決策は正しい秒数だけスリープすることだと思います。
これを bash で行うには、次の手順を実行します。
current_epoch=$(date +%s)
target_epoch=$(date -d '01/01/2010 12:00' +%s)
sleep_seconds=$(( $target_epoch - $current_epoch ))
sleep $sleep_seconds
精度をナノ秒 (実質的にはミリ秒程度) まで下げるには、たとえば次の構文を使用します。
current_epoch=$(date +%s.%N)
target_epoch=$(date -d "20:25:00.12345" +%s.%N)
sleep_seconds=$(echo "$target_epoch - $current_epoch"|bc)
sleep $sleep_seconds
macOS / OS X は秒未満の精度をサポートしていないことに注意してください。代わりにcoreutils
fromを使用する必要がありますbrew
→ これらの手順を参照してください
を使用sleep
しますが、 を使用して時間を計算しdate
ます。あなたはdate -d
これを使いたくなるでしょう。たとえば、次の週まで待ちたいとします。
expr `date -d "next week" +%s` - `date -d "now" +%s`
「来週」を待ちたい日付に置き換えてから、この式を値に割り当てて、その秒数だけスリープします。
startTime=$(date +%s)
endTime=$(date -d "next week" +%s)
timeToWait=$(($endTime- $startTime))
sleep $timeToWait
すべて完了!
これは、ジョブを実行し、ユーザーに残り時間を通知するソリューションです。私はほぼ毎日、夜間にスクリプトを実行するために使用しています ( cron
Windows で作業することができなかったので、cygwin を使用しています)。
&&
$ til 13:00 && date
1 hour and 18 minutes and 26 seconds left...
1 hour and 18 minutes left...
1 hour and 17 minutes left...
1 hour and 16 minutes left...
1 hour and 15 minutes left...
1 hour and 14 minutes left...
1 hour and 10 minutes left...
1 hour and 5 minutes left...
1 hour and 0 minutes left...
55 minutes left...
50 minutes left...
45 minutes left...
40 minutes left...
35 minutes left...
30 minutes left...
25 minutes left...
20 minutes left...
15 minutes left...
10 minutes left...
5 minutes left...
4 minutes left...
3 minutes left...
2 minutes left...
1 minute left...
Mon, May 18, 2015 1:00:00 PM
(最後の日付は関数の一部ではありませんが、ためです&& date
)
til(){
local hour mins target now left initial sleft correction m sec h hm hs ms ss showSeconds toSleep
showSeconds=true
[[ $1 =~ ([0-9][0-9]):([0-9][0-9]) ]] || { echo >&2 "USAGE: til HH:MM"; return 1; }
hour=${BASH_REMATCH[1]} mins=${BASH_REMATCH[2]}
target=$(date +%s -d "$hour:$mins") || return 1
now=$(date +%s)
(( target > now )) || target=$(date +%s -d "tomorrow $hour:$mins")
left=$((target - now))
initial=$left
while (( left > 0 )); do
if (( initial - left < 300 )) || (( left < 300 )) || [[ ${left: -2} == 00 ]]; then
# We enter this condition:
# - once every 5 minutes
# - every minute for 5 minutes after the start
# - every minute for 5 minutes before the end
# Here, we will print how much time is left, and re-synchronize the clock
hs= ms= ss=
m=$((left/60)) sec=$((left%60)) # minutes and seconds left
h=$((m/60)) hm=$((m%60)) # hours and minutes left
# Re-synchronise
now=$(date +%s) sleft=$((target - now)) # recalculate time left, multiple 60s sleeps and date calls have some overhead.
correction=$((sleft-left))
if (( ${correction#-} > 59 )); then
echo "System time change detected..."
(( sleft <= 0 )) && return # terminating as the desired time passed already
til "$1" && return # resuming the timer anew with the new time
fi
# plural calculations
(( sec > 1 )) && ss=s
(( hm != 1 )) && ms=s
(( h > 1 )) && hs=s
(( h > 0 )) && printf %s "$h hour$hs and "
(( h > 0 || hm > 0 )) && printf '%2d %s' "$hm" "minute$ms"
if [[ $showSeconds ]]; then
showSeconds=
(( h > 0 || hm > 0 )) && (( sec > 0 )) && printf %s " and "
(( sec > 0 )) && printf %s "$sec second$ss"
echo " left..."
(( sec > 0 )) && sleep "$sec" && left=$((left-sec)) && continue
else
echo " left..."
fi
fi
left=$((left-60))
sleep "$((60+correction))"
correction=0
done
}
プロセスに SIGSTOP シグナルを送信してプロセスの実行を停止し、SIGCONT シグナルを送信してプロセスの実行を再開させることができます。
そのため、SIGSTOP を送信してスクリプトを停止できます。
kill -SIGSTOP <pid>
次に、at デーモンを使用して、同じ方法で SIGCONT を送信することにより、それを起こします。
おそらく、あなたのスクリプトは、それ自体をスリープ状態にする前に、いつウェイクアップしたいのかを通知します。
SpoonMeiserの答えに従うために、ここに特定の例があります:
$cat ./reviveself
#!/bin/bash
# save my process ID
rspid=$$
# schedule my own resuscitation
# /bin/sh seems to dislike the SIGCONT form, so I use CONT
# at can accept specific dates and times as well as relative ones
# you can even do something like "at thursday" which would occur on a
# multiple of 24 hours rather than the beginning of the day
echo "kill -CONT $rspid"|at now + 2 minutes
# knock myself unconscious
# bash is happy with symbolic signals
kill -SIGSTOP $rspid
# do something to prove I'm alive
date>>reviveself.out
$
毎日同じパラメーターでスクリプトを実行できるように、時間と分のみをチェックするスクリプトが必要でした。明日が何日になるか心配したくない。そこで、別のアプローチを使用しました。
target="$1.$2"
cur=$(date '+%H.%M')
while test $target != $cur; do
sleep 59
cur=$(date '+%H.%M')
done
スクリプトのパラメーターは時間と分なので、次のように記述できます。
til 7 45 && mplayer song.ogg
(til はスクリプトの名前です)
仕事の遅刻が原因で日付を間違えることはもうありません。乾杯!
timeToWait = $(( $end - $start ))
「timeToWait」が負の数になる可能性があることに注意してください。(たとえば、「15:57」までスリープするように指定し、現在は「15:58」になっている場合)。したがって、奇妙なメッセージエラーを避けるためにチェックする必要があります:
#!/bin/bash
set -o nounset
### // Sleep until some date/time.
# // Example: sleepuntil 15:57; kdialog --msgbox "Backup needs to be done."
error() {
echo "$@" >&2
exit 1;
}
NAME_PROGRAM=$(basename "$0")
if [[ $# != 1 ]]; then
error "ERROR: program \"$NAME_PROGRAM\" needs 1 parameter and it has received: $#."
fi
current=$(date +%s.%N)
target=$(date -d "$1" +%s.%N)
seconds=$(echo "scale=9; $target - $current" | bc)
signchar=${seconds:0:1}
if [ "$signchar" = "-" ]; then
error "You need to specify in a different way the moment in which this program has to finish, probably indicating the day and the hour like in this example: $NAME_PROGRAM \"2009/12/30 10:57\"."
fi
sleep "$seconds"
# // End of file
現在とウェイクアップ時間の間の秒数を計算し、既存の「sleep」コマンドを使用できます。
おそらく、'at' を使用して、そのシグナルを待っているスクリプトにシグナルを送信できます。
function sleepuntil() {
local target_time="$1"
today=$(date +"%m/%d/%Y")
current_epoch=$(date +%s)
target_epoch=$(date -d "$today $target_time" +%s)
sleep_seconds=$(( $target_epoch - $current_epoch ))
sleep $sleep_seconds
}
target_time="11:59"; sleepuntil $target_time
複数のテストクライアントを同期するために、私が今書いたものを次に示します。
#!/usr/bin/python
import time
import sys
now = time.time()
mod = float(sys.argv[1])
until = now - now % mod + mod
print "sleeping until", until
while True:
delta = until - time.time()
if delta <= 0:
print "done sleeping ", time.time()
break
time.sleep(delta / 2)
このスクリプトは、次の「丸め」または「シャープ」時間までスリープします。
簡単な使用例は./sleep.py 10; ./test_client1.py
、ある端末と./sleep.py 10; ./test_client2.py
別の端末で実行することです。