Оскільки це питання було задано 4 роки тому, ця перша частина стосується старих версій bash:
Останнє редагування: середа, 22 квітня 2020 р., Щось між 10:30 та 10h: 55 (важливо для читання зразків)
Загальний метод (уникайте марних вилок!)
(Примітка: цей метод використовує, date -fякий не є POSIX, і не працює під MacOS! Якщо під Mac, перейдіть до мого чистогобашфункція )
Для того, щоб зменшити forks, замість того, щоб запускати dateдва рази, я вважаю за краще використовувати це:
Простий стартовий зразок
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
де tomorrow 21:30можуть бути замінені будь-якими датами та форматами, визнаними dateв майбутньому.
З високою точністю (наносек)
Майже те саме:
sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')
Досягнення наступного разу
Для досягнення наступногоHH:MM значення сьогодні, якщо це можливо, завтра, якщо пізно:
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Це працює під баш, кш та інші сучасні оболонки, але ви повинні використовувати:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
під легшими снарядами, якзола або тире.
Чисто баш шлях, ніякої вилки !!
Перевірено під MacOS!
Я написав одну дві маленькі функції: sleepUntilіsleepUntilHires
Syntax:
sleepUntil [-q] <HH[:MM[:SS]]> [more days]
-q Quiet: don't print sleep computed argument
HH Hours (minimal required argument)
MM Minutes (00 if not set)
SS Seconds (00 if not set)
more days multiplied by 86400 (0 by default)
Оскільки нові версії bash пропонують printfможливість отримати дату, для цього нового способу сну до HH: MM без використання dateабо будь-якої іншої вилки, я трохи побудувавбашфункція. Ось:
sleepUntil() {
local slp tzoff now quiet=false
[ "$1" = "-q" ] && shift && quiet=true
local -a 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 + ${2:-0}*86400
))
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp))
sleep $slp
}
Тоді:
sleepUntil 10:37 ; date +"Now, it is: %T"
sleep 49s, -> Wed Apr 22 10:37:00 2020
Now, it is: 10:37:00
sleepUntil -q 10:37:44 ; date +"Now, it is: %T"
Now, it is: 10:37:44
sleepUntil 10:50 1 ; date +"Now, it is: %T"
sleep 86675s, -> Thu Apr 23 10:50:00 2020
^C
Якщо ціль до цього, це буде спати до завтра:
sleepUntil 10:30 ; date +"Now, it is: %T"
sleep 85417s, -> Thu Apr 23 10:30:00 2020
^C
sleepUntil 10:30 1 ; date +"Now, it is: %T"
sleep 171825s, -> Fri Apr 24 10:30:00 2020
^C
Привіт час з баш під GNU / Linux
Останні баш, з версії 5.0 додати нову $EPOCHREALTIMEзмінну з мікросекундами. З цього є sleepUntilHiresфункція.
sleepUntilHires () {
local slp tzoff now quiet=false musec musleep;
[ "$1" = "-q" ] && shift && quiet=true;
local -a hms=(${1//:/ });
printf -v now '%(%s)T' -1;
IFS=. read now musec <<< $EPOCHREALTIME;
musleep=$[2000000-10
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+10#${hms[2]} -
tzoff - now - 1
) % 86400 ) + ${2:-0} * 86400
)).${musleep:1};
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1));
read -t $slp foo
}
Зверніть увагу: це використання, read -tяке вбудоване, а не sleep. На жаль, це не спрацює під час роботи у фоновому режимі без реального TTY. Не соромтеся замінити read -tна, sleepякщо ви плануєте запустити це у фонових скриптах ... (Але для фонового процесу подумайте про використання cronта / або atзамість всього цього)
Пропустити наступний абзац для тестів та попередження про $ËPOCHSECONDS!
Недавнє ядро уникайте використання /proc/timer_listкористувачем !!
Під останнім ядром Linux ви знайдете файл змінних з назвою `/ proc / timer_list`, де ви зможете прочитати зміщення` offset` і `now` за ** наносекунд **. Тож ми можемо обчислити час сну, щоб досягти * самого верхнього * бажаного часу.
(Я написав це для створення та відстеження конкретних подій у дуже великих файлах журналів, що містять тисячу рядків за одну секунду).
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
}
Після визначення двох змінних, доступних лише для читання , TIMER_LIST_OFFSETі TIMER_LIST_SKIP, функція дуже швидко отримає доступ до файлу змінної /proc/timer_listдля обчислення часу сну:
Маленька тестова функція
tstSleepUntilHires () {
local now next last
printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))
sleepUntilHires $next
date -f - +%F-%T.%N < <(echo now;sleep .92;echo now)
printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))
sleepUntilHires $next
date +%F-%T.%N
}
Може зробити щось на зразок:
sleep 0.244040s, -> Wed Apr 22 10:34:39 2020
2020-04-22-10:34:39.001685312
2020-04-22-10:34:39.922291769
sleep 0.077012s, -> Wed Apr 22 10:34:40 2020
2020-04-22-10:34:40.004264869
- На початку наступної секунди,
- час друку, тоді
- почекайте 0,92 секунди, тоді
- час друку, тоді
- обчислити 0,07 секунди, що залишилося, до наступної секунди
- спати 0,07 секунди, тоді
- час друку.
Обережно, щоб не змішувати $EPOCHSECONDі $EPOCHREALTIME!
Прочитайте моє попередження про різницю між $EPOCHSECONDі$EPOCHREALTIME
Використання цієї функції, $EPOCHREALTIMEтому не використовуйте $EPOCHSECONDдля встановлення наступної секунди :
Зразок випуску: Спроба друкувати час, округлений на 2 секунди:
for i in 1 2;do
printf -v nextH "%(%T)T" $(((EPOCHSECONDS/2)*2+2))
sleepUntilHires $nextH
IFS=. read now musec <<<$EPOCHREALTIME
printf "%(%c)T.%s\n" $now $musec
done
Може виробляти:
sleep 0.587936s, -> Wed Apr 22 10:51:26 2020
Wed Apr 22 10:51:26 2020.000630
sleep 86399.998797s, -> Thu Apr 23 10:51:26 2020
^C