Оскільки це питання було задано 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