109

特定の時間まで bash スクリプトをスリープさせたい。というわけで、「sleep」のようにインターバルなしで終了時刻を指定し、それまでスリープするコマンドが欲しいです。

特定の日時まで実行中のスクリプトをブロックする必要があるため、「at」デーモンは解決策ではありません。

そのようなコマンドはありますか?

4

19 に答える 19

115

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 は秒未満の精度をサポートしていないことに注意してください。代わりにcoreutilsfromを使用する必要がありますbrewこれらの手順を参照してください

于 2009-03-14T14:47:17.040 に答える
23

を使用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

すべて完了!

于 2009-03-14T14:46:23.390 に答える
10

これは、ジョブを実行し、ユーザーに残り時間を通知するソリューションです。私はほぼ毎日、夜間にスクリプトを実行するために使用しています ( cronWindows で作業することができなかったので、cygwin を使用しています)。

特徴

  • 秒単位まで正確に
  • システム時刻の変更を検出して適応
  • 残り時間を知らせるインテリジェントな出力
  • 24時間入力形式
  • チェーンできるように 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
}
于 2015-05-18T09:07:34.000 に答える
8

プロセスに SIGSTOP シグナルを送信してプロセスの実行を停止し、SIGCONT シグナルを送信してプロセスの実行を再開させることができます。

そのため、SIGSTOP を送信してスクリプトを停止できます。

kill -SIGSTOP <pid>

次に、at デーモンを使用して、同じ方法で SIGCONT を送信することにより、それを起こします。

おそらく、あなたのスクリプトは、それ自体をスリープ状態にする前に、いつウェイクアップしたいのかを通知します。

于 2009-03-14T15:19:30.160 に答える
7

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
$
于 2009-04-20T18:31:53.023 に答える
7

毎日同じパラメーターでスクリプトを実行できるように、時間と分のみをチェックするスクリプトが必要でした。明日が何日になるか心配したくない。そこで、別のアプローチを使用しました。

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 はスクリプトの名前です)

仕事の遅刻が原因で日付を間違えることはもうありません。乾杯!

于 2010-01-19T03:47:43.833 に答える
4

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
于 2009-05-05T09:55:32.470 に答える
1

現在とウェイクアップ時間の間の秒数を計算し、既存の「sleep」コマンドを使用できます。

于 2009-03-14T14:39:32.160 に答える
1

おそらく、'at' を使用して、そのシグナルを待っているスクリプトにシグナルを送信できます。

于 2009-03-14T15:09:17.440 に答える
0
 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
于 2016-07-19T17:29:33.633 に答える
0

複数のテストクライアントを同期するために、私が今書いたものを次に示します。

#!/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別の端末で実行することです。

于 2013-06-11T12:41:05.297 に答える