特定の時間までbashスクリプトをスリープさせたい。したがって、終了時間以外の間隔をとらずにそれまでスリープする「スリープ」のようなコマンドが必要です。
「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
から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
出来た!
この質問は4年前に尋ねられたので、この最初の部分はold bashバージョンに関するものです:
forks
を2回実行する代わりに、date
を減らすために、これを使用することを好みます。
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
ここで、tomorrow 21:30
は、将来date
で認識されるあらゆる種類の日付と形式に置き換えることができます。
以上の場合、next HH:MM
に到達すると、可能な場合は今日、遅すぎる場合は明日を意味します。
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
これは bash 、 ksh およびその他の最新のシェルの下で機能しますが、次を使用する必要があります。
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
この種のツールの必要性は24時間を超えたことがないため、以下はHH
、HH:MM
、またはHH:MM:SS
構文の意味のみに関係します次の24時間 。 (とにかくもっと必要な場合は、date
へのフォークを使用して古いメソッドに戻ることもできます。何日も実行されているスクリプトで1つのフォークを削除しようとするのはやり過ぎです。)
Bashの新しいバージョンでは、日付を取得するprintf
オプションが提供されるため、この新しい方法HH:MMまでスリープするdate
またはその他のフォークを使用せずに、少し bash 関数を作成しました。ここにあります:
sleepUntil() {
local slp tzoff now quiet=false
[ "$1" = "-q" ] && shift && quiet=true
local hms=(${1//:/ })
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T\n' $now
tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))
slp=$(((86400+(now-now%86400)+10#$hms*3600+10#${hms[1]}*60+${hms[2]}-tzoff-now)%86400))
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp))
sleep $slp
}
次に:
sleepUntil 11:11 ; date +"Now, it is: %T"
sleep 3s, -> sam 28 sep 2013 11:11:00 CEST
Now, it is: 11:11:00
sleepUntil -q 11:11:5 ; date +"Now, it is: %T"
Now, it is: 11:11:05
最近のLinuxカーネルでは、/proc/timer_list
という名前の変数ファイルがあり、offset
およびnow
変数をnanoseconds。したがって、スリープ時間を計算してvery top希望の時間に到達することができます。
(1秒間に数千行を含む非常に大きなログファイルで特定のイベントを生成および追跡するためにこれを作成しました)。
mapfile </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
[[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_SKIP=$_i
[[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
break
done
unset _i _timer_list
readonly TIMER_LIST_OFFSET TIMER_LIST_SKIP
sleepUntilHires() {
local slp tzoff now quiet=false nsnow nsslp
[ "$1" = "-q" ] && shift && quiet=true
local hms=(${1//:/ })
mapfile -n 1 -s $TIMER_LIST_SKIP nsnow </proc/timer_list
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T\n' $now
nsnow=$((${nsnow//[a-z ]}+TIMER_LIST_OFFSET))
nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))
slp=$(( ( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#${hms[1]}*60+${hms[2]} -
tzoff - now - 1
) % 86400)).${nsslp:1}
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1))
sleep $slp
}
2つの読み取り専用変数、TIMER_LIST_OFFSET
およびTIMER_LIST_SKIP
を定義した後、関数はスリープ時間を計算するために変数ファイル/proc/timer_list
に非常にすばやくアクセスします。
sleepUntilHires 15:03 ;date +%F-%T.%N ;sleep .97;date +%F-%T.%N
sleep 19.632345552s, -> sam 28 sep 2013 15:03:00 CEST
2013-09-28-15:03:00.003471143
2013-09-28-15:03:00.976100517
sleepUntilHires -q 15:04;date -f - +%F-%T.%N < <(echo now;sleep .97;echo now)
2013-09-28-15:04:00.003608002
2013-09-28-15:04:00.974066555
そして最後に
tstSleepUntilHires () {
local now next last
printf -v now "%(%s)T"
printf -v next "%(%H:%M:%S)T" $((now+1))
printf -v last "%(%H:%M:%S)T" $((now+2))
sleepUntilHires $next
date -f - +%F-%T.%N < <(echo now;sleep .92;echo now)
sleepUntilHires $last
date +%F-%T.%N
}
次のようにレンダリングする場合があります
sleep 0.155579469s, -> Mon Aug 20 20:42:51 2018
2018-08-20-20:42:51.005743047
2018-08-20-20:42:51.927112981
sleep 0.071764300s, -> Mon Aug 20 20:42:52 2018
2018-08-20-20:42:52.003165816
注意:.92 + 0.071 = .991
(デスクトップ上)
SIGSTOPシグナルを送信してプロセスの実行を停止し、SIGCONTシグナルを送信して実行を再開できます。
したがって、SIGSTOPを送信することでスクリプトを停止できます。
kill -SIGSTOP <pid>
次に、at deamonを使用して、同じ方法でSIGCONTを送信して起動します。
おそらく、スクリプトは、自身をスリープ状態にする前に、いつ目覚めさせたいかを通知します。
Ubuntu 12.04.4 LTSでは、次の単純なbash入力が機能します。
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
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はスクリプトの名前です)
仕事に遅れる日はもうありません。乾杯!
これは、ジョブを実行し、残り時間をユーザーに通知するソリューションです。私は夜中にスクリプトを実行するためにほぼ毎日それを使用します(ウィンドウでcron
を動作させることができなかったため、cygwinを使用します)
&&
と連鎖できるようにtrueを返します$ 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
}
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
これを行うために Hypnos という小さなユーティリティをまとめました。 crontab構文を使用して構成され、それまでブロックされます。
#!/bin/bash
while [ 1 ]; do
hypnos "0 * * * *"
echo "running some tasks..."
# ...
done
OpenBSDでは、次を使用して*/5
5分間 crontab(5)
を圧縮できます。00
の1時間ごとのジョブ(正確な間隔で同じタスクを実行しながら、生成される電子メールの数を減らすため):
#!/bin/sh -x
for k in $(jot 12 00 55)
do
echo $(date) doing stuff
sleep $(expr $(date -j +%s $(printf %02d $(expr $k + 5))) - $(date -j +%s))
done
date(1)
は、最後の反復で設計により sleep(1)
を破壊することに注意してください。 60
分は有効な時間ではないため(そうでない場合!)、メールレポートを取得する前に余分な時間を待つ必要はありません。
また、反復の1つに5分以上が割り当てられている場合、sleep
も同様に設計上、まったくスリープしないことで優雅に失敗します(負の数はコマンドラインオプションとして解釈されるため) 、次の時間または永遠に折り返すのではなく)、割り当てられた時間内にジョブが完了することを確認します(たとえば、反復の1つだけが5分以上かかる場合は、次の1時間まで何もラップすることなく、追いつく時間です。
printf(1)
が必要なのは、date
が分指定に正確に2桁を想定しているためです。
これは、複数のテストクライアントを同期するために今書いたものです。
#!/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
1つの端末で./sleep.py 10; ./test_client2.py
別の。
この正確な目的のために、実際に https://tamentis.com/projects/sleepuntil/ を書きました。ほとんどのコードはBSDの「at」に由来するため、少し過剰になりすぎているため、かなり標準に準拠しています。
$ sleepuntil noon && sendmail something