Le Wed, Dec 03, 2025 at 09:59:40AM -0700, Stan Marsh a écrit : > From: Chet Ramey > Subject: Re: Philosophical bash(1) bug > > (It's not really a float -- it's seconds and microseconds separated by a > > `.').
Using *fixed float* is like using *integer* and add a radix at fixed emplacment. > > Greg's results (as well as my own testing) suggest otherwise. That a > TMOUT value of 3.1 means 3.1 (decimal) seconds. > Unless "time" is also lying to us... I don't know if it's "time()", or "gettimeofday()", but! (See: https://lists.gnu.org/archive/html/bug-bash/2020-04/msg00075.html ) I was trying to create a function to reach next *rounded period in nanosecond agains UNIXEPOCH*, for monitoring, something like: isUnsignedNumber () { case $1 in '' | . | *[!0-9.]* | *.*.*) echo "ERROR: $1 is not a number" 1>&2 return 1 ;; esac } sleepNext () { isUnsignedNumber $1 || return 1 local _reqSlp _toSlp printf -v _reqSlp %.6f "$1" _reqSlp=$((10#${_reqSlp/.})) _toSlp=00000$(( _reqSlp - ( ${EPOCHREALTIME/.} % _reqSlp ) )) printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6} ! read -sn 1 -t $_toSlp ${2:-_} } $ while sleepNext .5;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus"; done Fri 05 11:25:34.500606 Fri 05 11:25:35.000273 Fri 05 11:25:35.500798 Fri 05 11:25:36.000776 Fri 05 11:25:36.500764 All new expansion of EPOCHREALTIME was done less than 1'000 nanoseconds after *rounded period*, this look like expected! But with bigger timeout, this "delay" will grown: $ while sleepNext 10;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus"; done Fri 05 11:25:50.002760 Fri 05 11:26:00.006771 Fri 05 11:26:10.006732 Fri 05 11:26:20.006705 With a timeout of 10 seconds, this delay become bigger than 6'000 nanoseconds! $ while sleepNext 900;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus"; done Fri 05 12:15:00.098612 Fri 05 12:30:00.098717 ... Where this "gap" become less and less negligible! To work around this, my solution is (for now, with two tunneable variables): sleepNextHR() { isUnsignedNumber $1 || return 1 local -ir _minSleepNS=1'000'00 _initialPercentSleep=96 # May be tunned! local _reqSlp _toSlp while printf -v _reqSlp %.6f "$1" _reqSlp=$((10#${_reqSlp/.})) _toSlp=00000$(( _reqSlp - ( ${EPOCHREALTIME/.} % _reqSlp ) )) printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6} (( 10#${_toSlp/.} > _minSleepNS )); do _toSlp=00000$(( 10#${_toSlp/.} * _initialPercentSleep / 100 )) printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6} read -sn 1 -t $_toSlp ${2:-_} && return 1 done ! read -sn 1 -t $_toSlp ${2:-_} } $ while sleepNextHR .5;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus"; done Fri 05 12:36:03.000173 Fri 05 12:36:03.500183 Fri 05 12:36:04.000189 Fri 05 12:36:04.500188 $ while sleepNextHR 10;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus"; done Fri 05 12:37:00.000188 Fri 05 12:37:10.000208 Fri 05 12:37:20.000179 Trick for tracing this: $ BASH_XTRACEFD=$marker $ set -x $ while sleepNextHR .5;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '\e[1m%(%a %d %T)T.%s\e[0m\n' "$nowSec" "$nowMus"; done + read -sn 1 -t 0.142758 _ + read -sn 1 -t 0.005446 _ Fri 05 12:48:14.500337 + read -sn 1 -t 0.479283 _ + read -sn 1 -t 0.019118 _ Fri 05 12:48:15.000388 + read -sn 1 -t 0.479083 _ + read -sn 1 -t 0.019041 _ Fri 05 12:48:15.500503 + read -sn 1 -t 0.478937 _ + read -sn 1 -t 0.019060 _ Fri 05 12:48:16.000308 $ while sleepNextHR 10;do IFS=. read nowSec nowMus <<<$EPOCHREALTIME; printf '\e[1m%(%a %d %T)T.%s\e[0m\n' "$nowSec" "$nowMus"; done + read -sn 1 -t 5.435048 _ + read -sn 1 -t 0.212304 _ + read -sn 1 -t 0.008227 _ Fri 05 12:48:40.000235 + read -sn 1 -t 9.599453 _ + read -sn 1 -t 0.377568 _ + read -sn 1 -t 0.015063 _ Fri 05 12:48:50.000250 + read -sn 1 -t 9.599509 _ + read -sn 1 -t 0.377583 _ + read -sn 1 -t 0.015073 _ Fri 05 12:49:00.000264 Where using `set -x` will have some impact (~190ns without, ~300ns with "-x", but still acceptable for my needs) $ set -x;unset BASH_XTRACEFD;exec {marker}>&- So as I said, I don't know who are lying, but I don't really care. -- Félix Hauri - <[email protected]> - http://www.f-hauri.ch
