On my machine I need to execute a cycle that iterates 1 simple command that must have a delay expressed in fractions of second.
Let’s say that I need:
- to save a file with an increasing enumaration ( file-0, file-1, file-2, … ) generate by something trivial for this example like
time > file-$x - I need to do this every 1/70 of second (as an example) because I would like to express my time with fractions of second.
How can I be really precise and have everything expressed with a bash script?
The fraction can generate an indeterminable quantity, I need to be precise and so I need at least 4-5 decimals.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
To convert from fractions to decimals in bash, do something like
myvar=$(echo "scale=4; 5/10" | bc)
Then, to do a loop on that value,
for i in $(seq 1 1000); do sleep $myvar; done
My sleep implementation on Debian (GNU) seem to accept decimal sleep values.
Unfortunately..
With that kind of precision (4-5 decimal places), you’re going to want something like a perl script or a compiled program; the overhead of calling any program within the loop is going to add a lot of jitter. Calling sleep itself will take a few milliseconds.
Consider the following, a 1/10,000ths of a second sleep, done 1000 times:
time for i in $(seq 1 1000); do sleep 0.0001; done real 0m2.099s user 0m3.080s sys 0m1.464s
The expected result would be 1/10th of a second. Sleep has nowhere near the tolerances you want.
https://stackoverflow.com/questions/896904/how-do-i-sleep-for-a-millisecond-in-perl
using perl’s Time::HiRes, 1000*1000 microseconds:
my $i=0;
for($i=0;$i<=1000;$i++) {
usleep(1000);
}
real 0m1.133s
user 0m0.024s
sys 0m0.012s
gives us much closer to a second.
Method 2
Maybe you can simply run
sleep 0.7
?
man 1 sleep
on my archlinux distro :
DESCRIPTION
Pause for NUMBER seconds. SUFFIX may be ‘s’ for seconds (the default), ‘m’ for minutes, ‘h’ for hours or ‘d’ for days. Unlike
most implementations that require NUMBER be an integer, here
NUMBER may be an arbitrary floating point number. Given two or more arguments, pause for the amount of time specified by the sum
of their values.
Method 3
Spawning a process and load a new executable in it is likely to take a few miliseconds, so that kind of precision doesn’t really make sense. Also note that CPU time on many systems is allocated to processes by slices of up to 10ms.
Having said that, some sleep implementations take fractional numbers of seconds, and both zsh and ksh93 can make their $SECONDS special variable fractional with typeset -F SECONDS.
Example (zsh):
$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((1./70)); date +%s.%N; done | { head -n3;echo ..;tail -n3; }; echo $SECONDS
1350076317.374870501
1350076317.391034397
1350076317.407278461
..
1350076318.464585550
1350076318.480887660
1350076318.497133050
1.1393780000
Oops, it drifted. You can adjust the sleeping time based on $SECONDS:
$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((i/70. - SECONDS)); date +%s.%N; done | { head -n3;echo ...;tail -n3; }; echo $SECONDS
1350076420.262775654
1350076420.277012997
1350076420.291302750
../..
1350076421.219682227
1350076421.234134663
1350076421.248255685
1.0020580000
Those 2 extra miliseconds are probably to be accounted to running the last sleep and date commands.
Also note that zsh has a zselect builtin with timeout expressed in hundredth of a second. And ksh93 has sleep built in (and accepts floating points) and its printf can print date/times.
$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do ((i<4 || i>67)) && printf '%(%S.%N)Tn' now; sleep $((i/70.-SECONDS)); done; echo $SECONDS 20.823349000 20.837510000 20.851663000 21.780099000 21.794254000 21.808405000 0.9992358685
If you want anything more precise, you’ll probably want a real time operating system or an operating system with real time capabilities and certainly not use a shell.
Method 4
If your shell’s sleep doesn’t accept fraction, use perl.
sleep_fraction() {
/usr/bin/perl -e "select(undef, undef, undef, $1)"
}
sleep_fraction 0.01428
If you need to find out the fraction, use echo "scale=5; 1/70" | bc
Method 5
Under Alpine Linux (Busybox) you can loop for microseconds with usleep 10 (equivalent to 0.00001 of a second)
GNU sleep supports fractions of a second: sleep 0.00001
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0