Just trying to keep track of the build time for a bash script, to the 1/10th of a second
I am looking for something like:
START_TIME=$(date)
sleep 5;
END_TIME=$(date)-${START_TIME};
and round it to a tenth of a second.
How can I do this?
You can use date with nanosecond and truncate to one char
#!/bin/bash
START=$(date "+%s%1N")
sleep 2
END=$(date "+%s%1N")
echo "The difference is $((END-START))"
Related
I want to round down the minutes to the nearest 15 min interval i.e. 00,15,30,45. I'm currently doing the below:
echo $(date +'%Y/%m/%d/%H/')$((($(($(date +'%M') / 15))-0)*15))
But at the start of the hour between 1-14 minutes, I get "/2021/11/03/21/0" instead of 00.
Also, I'm not sure if this is the best way to do this. Are there any alternatives?
Would you please try the following:
mod=$(( 10#$(date +%M) \% 15 ))
date -d "-${mod} minutes" +%Y/%m/%d/%H/%M
The variable mod holds the remainder of the minutes divided by 15.
Then round down to the nearest 15 minute interval by subtracting mod.
[Edit]
The manpage of crontab says:
Percent-signs (%) in the command, unless
escaped with backslash (), will be changed into newline
characters, and all data after the first % will be sent to
the command as standard input.
If you want to execute the command within crontab, please modify the command as:
mod=$(( 10#$(date +\%M) \% 15 ))
date -d "-${mod} minutes" +\%Y/\%m/\%d/\%H/\%M
[Edit2]
If you want to embed the code in crontab file, please add a line which look like:
0 12 * * * username bash -c 'mod=$(( 10#$(date +\%M) \% 15 )); DATEVAR=$(date -d "-${mod} minutes" +\%Y/\%m/\%d/\%H/\%M); write.sh "$DATEVAR"'
Please modify the execution time/date and the username accordingly.
The default shell to execute crontab command may be /bin/sh. Then you will need to explicitly switch it to /bin/bash to execute bash commands.
My apology that a backslash in front of % 15 (modulo operation) was missing in my previous post.
Another approach:
min=$(printf "%0.2d" $(( ($(date +'%M') / 15) * 15 )))
echo "$(date +'%Y/%m/%d/%H/')$min"
date -d "#$((($(date +%s) + 450) / 900 * 900))"
This uses the properties of integer division to “subtract a modulus” and adds half of the desired interval to (almost) mimic a rounding operation.
A bit of extra sub-second rounding precision (for no good reason) can be achieved by taking %N (nanoseconds) into account. But it will not matter, because the exact half of the rounding interval (450 seconds) is already aligned with the default epoch resolution (1 second). (If the number of seconds in the desired rounding interval was odd, then the following would increase the time rounding precision.)
date -d "#$((($(date +%s%N) + 45*10**10) / (9*10**11) * 900))"
Pure bash, bash version 4.3 or higher:
printf '%(%Y/%m/%d/%H/%M)T\n' "$(( $(printf '%(%s)T') /(15*60)*(15*60) ))"
Using GNU date (any bash version or POSIX shell):
date -d #$(( $(date +%s) /(15*60)*(15*60) )) +%Y/%m/%d/%H/%M
Truncates the current epoch date (seconds since 1970-01-01 00:00:00) to a 15 minute (900 second) interval, then converts to desired format.
Retrieves the current date/time once only.
If you build a date/time from two separate date/times, it can be wrong, when a unit ticks over in between.
The printf date-time format string was added in bash 4.2, and was changed in 4.3 to also print the current time, if no input date was given.
Note that bash arithmetic treats numbers that start with zero as octals, and numbers like 08 and 09 will cause an error (because they are not octal numbers).
I looked all over online and can't seem to find anything that addresses what I am trying to do. I am using Bash on an Unbuntu VM.
I created the following script
start_code=$(date +%H:%M:%S)
end_code=$(date +%H:%M:%S)
echo $start_code
for i in {1..1000};
do echo $RANDOM >> filename.txt;
done
echo $end_code
The code works fine, but is there any way that I can subtract the variables start_code from end_code??
I have tried this many different ways one being total_code=$(($start_code - $end_code))
but I get errors or nothing returned with everything that I have tried. As I'm brand new to Bash and I'm not even sure if I can do this. Any help would be greatly appreciated.
There are better ways to compute time lapsed. You can do the subtraction with something like:
$ cat a.sh
#!/bin/sh
foo() {
# This function does stuff
sleep ${1-2}
}
start=$(date +%s)
foo 3
end=$(date +%s)
echo "time lapsed: $((end - start))"
time foo 4
$ ./a.sh
time lapsed: 3
real 0m4.006s
user 0m0.001s
sys 0m0.001s
In the above, we first store a time stamp before calling the function foo that just sleeps for 3 seconds. Then we compute a new time stamp and subtract. Then we do the same using the time builtin and let the function sleep for 4 seconds.
I'd like to run a bash script, for use on a Raspberry Pi, that says "if the seconds of the current time is exactly 00 or 30, then do X".
I've googled and found some suggestions to use cron, but I think there'd be a small delay at the start which I want to avoid.
Thanks
If you don't like the delay of cron, which is mostly for background stuff, you could loop in the foreground:
while true; do
d=$(date +%S)
if [ $d -eq 0 -o $d -eq 30 ]; then
# command here
date +%S.%N
# replace the above command with whatever you want
sleep 5
else
sleep 0.001
fi
done
The Linux date command can check the current system clock quite quickly. I've used this loop to print the nanosecond timer with data to demonstrate the low latency. That is, on my system, I get:
30.001057483
00.003022980
30.003011572
and so on.
I have just started learning shell script recently, so I don't know much about it.
I am trying to find example of time based while loop but not having any luck.
I want to run a loop for specific amount of time, let's say 1 hour. So loop runs for an hour and then ends automatically.
Edit: This loop will run continiously without any sleep, so the loop condition should be based on loop's start time and current time, not on sleep.
The best way to do this is using the $SECONDS variable, which has a count of the time that the script (or shell) has been running for. The below sample shows how to run a while loop for 3 seconds.
#! /bin/bash
end=$((SECONDS+3))
while [ $SECONDS -lt $end ]; do
# Do what you want.
:
done
Caveat: All solutions in this answer - except the ksh one - can return up to (but not including) 1 second early, since they're based on an integral-seconds counter that advances based on the real-time (system) clock rather than based on when code execution started.
bash, ksh, zsh solution, using special shell variable $SECONDS:
Slightly simplified version of #bsravanin's answer.
Loosely speaking, $SECONDS contains the number of seconds elapsed so far in a script.
In bash and zsh you get integral seconds advancing by the pulse of the system (real-time) clock - i.e., counting behind the scenes does not truly start at 0(!), but at whatever fraction since the last full time-of-day second the script happened to be started at or the SECONDS variable was reset.
By contrast, ksh operates as one would expect: counting truly starts at 0 when you reset $SECONDS; furthermore, $SECONDS reports fractional seconds in ksh.
Therefore, the only shell in which this solution works reasonably predictably and precisely is ksh. That said, for rough measurements and timeouts it may still be usable in bash and zsh.
Note: The following uses a bash shebang line; simply substituting ksh or zsh for bash will make the script run with these shells, too.
#!/usr/bin/env bash
secs=3600 # Set interval (duration) in seconds.
SECONDS=0 # Reset $SECONDS; counting of seconds will (re)start from 0(-ish).
while (( SECONDS < secs )); do # Loop until interval has elapsed.
# ...
done
Solution for POSIX-features-only shells, such as sh (dash) on Ubuntu ($SECONDS is not POSIX-compliant)
Cleaned-up version of #dcpomero's answer.
Uses epoch time returned by date +%s (seconds elapsed since 1 January 1970) and POSIX syntax for the conditional.
Caveat: date +%s itself (specifically, the %s format) is not POSIX-compliant, but it'll work on (at least) Linux, FreeBSD, and OSX.
#!/bin/sh
secs=3600 # Set interval (duration) in seconds.
endTime=$(( $(date +%s) + secs )) # Calculate end time.
while [ $(date +%s) -lt $endTime ]; do # Loop until interval has elapsed.
# ...
done
You can try this
starttime = `date +%s`
while [ $(( $(date +%s) - 3600 )) -lt $starttime ]; do
done
where 'date +%s' gives the current time in seconds.
You can use the loop command, available here, like so:
$ loop './do_thing.sh' --for-duration 1h --every 5s
Which will do the your thing every five seconds for one hour.
date +%s will give you the seconds since the epoch, so something like
startTime = `date +%s`
timeSpan = #some number of seconds
endTime = timeSpan + startTime
while (( `date +%s` < endTime )) ; do
#code
done
You might need some edits, since my bash is rusty
You can explore the -d option of date.
Below is a shell script snippet to exemplify. It is similar to other answers, but may be more useful in different scenarios.
# set -e to exit if the time provided by argument 1 is not valid for date.
# The variable stop_date will store the seconds since 1970-01-01 00:00:00
# UTC, according to the date specified by -d "$1".
set -e
stop_date=$(date -d "$1" "+%s")
set +e
echo -e "Starting at $(date)"
echo -e "Finishing at $(date -d "$1")"
# Repeat the loop while the current date is less than stop_date
while [ $(date "+%s") -lt ${stop_date} ]; do
# your commands that will run until stop_date
done
You can then call the script in the many different ways date understands:
$ ./the_script.sh "1 hour 4 minutes 3 seconds"
Starting at Fri Jun 2 10:50:28 BRT 2017
Finishing at Fri Jun 2 11:54:31 BRT 2017
$ ./the_script.sh "tomorrow 8:00am"
Starting at Fri Jun 2 10:50:39 BRT 2017
Finishing at Sat Jun 3 08:00:00 BRT 2017
$ ./the_script.sh "monday 8:00am"
Starting at Fri Jun 2 10:51:25 BRT 2017
Finishing at Mon Jun 5 08:00:00 BRT 2017
This is exactly what I was looking for,
here is a one line solution based on bsravanin's answer:
end=$((SECONDS+30)); of=$((end-SECONDS)) ; while [ $SECONDS -lt $end ]; do echo $((end-SECONDS)) seconds left of $of ; sleep 1 ; done;
For a more modern approach...
Bash
declare -ir MAX_SECONDS=5
declare -ir TIMEOUT=$SECONDS+$MAX_SECONDS
while (( $SECONDS < $TIMEOUT )); do
# foo
done
Korn
typeset -ir MAX_SECONDS=5
typeset -ir TIMEOUT=$SECONDS+$MAX_SECONDS
while (( $SECONDS < $TIMEOUT )); do
# bar
done
I'd like to use the time command in a bash script to calculate the elapsed time of the script and write that to a log file. I only need the real time, not the user and sys. Also need it in a decent format. e.g 00:00:00:00 (not like the standard output). I appreciate any advice.
The expected format supposed to be 00:00:00.0000 (milliseconds) [hours]:[minutes]:[seconds].[milliseconds]
I've already 3 scripts. I saw an example like this:
{ time { # section code goes here } } 2> timing.log
But I only need the real time, not the user and sys. Also need it in a decent format. e.g 00:00:00:00 (not like the standard output).
In other words, I'd like to know how to turn the time output into something easier to process.
You could use the date command to get the current time before and after performing the work to be timed and calculate the difference like this:
#!/bin/bash
# Get time as a UNIX timestamp (seconds elapsed since Jan 1, 1970 0:00 UTC)
T="$(date +%s)"
# Do some work here
sleep 2
T="$(($(date +%s)-T))"
echo "Time in seconds: ${T}"
printf "Pretty format: %02d:%02d:%02d:%02d\n" "$((T/86400))" "$((T/3600%24))" "$((T/60%60))" "$((T%60))""
Notes:
$((...)) can be used for basic arithmetic in bash – caution: do not put spaces before a minus - as this might be interpreted as a command-line option.
See also: http://tldp.org/LDP/abs/html/arithexp.html
EDIT:
Additionally, you may want to take a look at sed to search and extract substrings from the output generated by time.
EDIT:
Example for timing with milliseconds (actually nanoseconds but truncated to milliseconds here). Your version of date has to support the %N format and bash should support large numbers.
# UNIX timestamp concatenated with nanoseconds
T="$(date +%s%N)"
# Do some work here
sleep 2
# Time interval in nanoseconds
T="$(($(date +%s%N)-T))"
# Seconds
S="$((T/1000000000))"
# Milliseconds
M="$((T/1000000))"
echo "Time in nanoseconds: ${T}"
printf "Pretty format: %02d:%02d:%02d:%02d.%03d\n" "$((S/86400))" "$((S/3600%24))" "$((S/60%60))" "$((S%60))" "${M}"
DISCLAIMER:
My original version said
M="$((T%1000000000/1000000))"
but this was edited out because it apparently did not work for some people whereas the new version reportedly did. I did not approve of this because I think that you have to use the remainder only but was outvoted.
Choose whatever fits you.
To use the Bash builtin time rather than /bin/time you can set this variable:
TIMEFORMAT='%3R'
which will output the real time that looks like this:
5.009
or
65.233
The number specifies the precision and can range from 0 to 3 (the default).
You can use:
TIMEFORMAT='%3lR'
to get output that looks like:
3m10.022s
The l (ell) gives a long format.
From the man page for time:
There may be a shell built-in called time, avoid this by specifying /usr/bin/time
You can provide a format string and one of the format options is elapsed time - e.g. %E
/usr/bin/time -f'%E' $CMD
Example:
$ /usr/bin/time -f'%E' ls /tmp/mako/
res.py res.pyc
0:00.01
Use the bash built-in variable SECONDS. Each time you reference the variable it will return the elapsed time since the script invocation.
Example:
echo "Start $SECONDS"
sleep 10
echo "Middle $SECONDS"
sleep 10
echo "End $SECONDS"
Output:
Start 0
Middle 10
End 20
Not quite sure what you are asking, have you tried:
time yourscript | tail -n1 >log
Edit: ok, so you know how to get the times out and you just want to change the format. It would help if you described what format you want, but here are some things to try:
time -p script
This changes the output to one time per line in seconds with decimals. You only want the real time, not the other two so to get the number of seconds use:
time -p script | tail -n 3 | head -n 1
The accepted answer gives me this output
# bash date.sh
Time in seconds: 51
date.sh: line 12: unexpected EOF while looking for matching `"'
date.sh: line 21: syntax error: unexpected end of file
This is how I solved the issue
#!/bin/bash
date1=$(date --date 'now' +%s) #date since epoch in seconds at the start of script
somecommand
date2=$(date --date 'now' +%s) #date since epoch in seconds at the end of script
difference=$(echo "$((date2-$date1))") # difference between two values
date3=$(echo "scale=2 ; $difference/3600" | bc) # difference/3600 = seconds in hours
echo SCRIPT TOOK $date3 HRS TO COMPLETE # 3rd variable for a pretty output.