Simultaneous commands - bash

I have a script:
#!/bin/bash
date +%T &
Hours=`date +"%H"` &
Minutes=`date +"%M"` &
Seconds=`date +"%S"`
echo "$Hours:$Minutes:$Seconds"
The objective is to echo date two times and then take out hours, minutes, seconds and calculate how many seconds elapsed between those two commands. So my solution is to write hours, minutes, seconds into variables, then work with those variables.
Problem: echo only echoes seconds which means my interpretation of & is wrong.
How can I fix the & problem? I need those commands to run simultaneously so I can check.
date +%s apparently won't work on certain inputs like:
Wed Mar 4 10:34:59 2015
Wed Mar 4 10:35:08 2015
Will give result of 00:00:01 instead of 00:00:09 or:
Wed Mar 4 10:34:59 2015
Wed Mar 4 17:43:08 2015
will give the result of 12:13:14 instead of 07:08:09. Is it true? Or can I use date +%s and then decrease those two outputs?

You don't need to run the commands simultaneously. Run just one command:
read hours minutes seconds < <( date '+%H %M %S' )
But it can be even simpler: just use the +%s format to get number of seconds since the epoch. You'll get two numbers you can safely subtract.
#!/bin/bash
start=$(date +%s)
sleep 10
end=$(date +%s)
echo The command took $(( end - start )) seconds.
The easiest way is to use the shell variable $SECONDS.
Each time this parameter is referenced, the number of seconds since shell invocation is returned. If a value is assigned to SECONDS, the value returned upon
subsequent references is the number of seconds since the assignment plus the
value assigned. If SECONDS is unset, it loses its special properties, even if
it is subsequently reset.

the main problem is that in this case the command date will be executed tree times to get the values..
so you don't need to execute the commad date too many times
#!/bin/bash
#date +%T &
function myTime(){
now=`date +"%H:%M:%S" &`
}
myTime
s1=`echo $now | cut -d":" -f2`
myTime
s2=`echo $now | cut -d":" -f2`
echo "s1[$s1] - s2[$s2]"
Then you can apply your rules to verify the time elapsed
Regards
Claudio

Related

Calculating PowerShell ticks in Bash

I am translating a PowerShell script in Bash.
This is how the ticks for current datetime are obtained in PowerShell:
[System.DateTime]::Now.Ticks;
By following the definition of Ticks, this is how I am trying to approximate the same calculation using the date command in bash:
echo $(($(($(date -u '+%s') - $(date -d "0001-01-01T00:00:00.0000000 UTC" '+%s'))) * 10000000 ))
This is what I got the last time I tried:
$ echo $(($(($(date -u '+%s') - $(date -d "0001-01-01T00:00:00.0000000 UTC" '+%s'))) * 10000000 )) ; pwsh -c "[System.DateTime]::Now.Ticks;"
637707117310000000
637707189324310740
In particular, the first 7 digits are identical, but digits in position 8 and 9 are still too different between the two values.
I calculated that this means there is just a 2 hours difference between the 2 values. But why? It cannot be the timezone, since I specified UTC timezone in both date commands, right? What do you think?
Note: my suspects about the timezone are increasing, since I am currently based in UTC+2 (therefore 2 hours difference from UTC), but how is this possible since I explicitly specified UTC as timezone in the date commands?
Solved it! The problem wasn't in the date commands, it was in the PowerShell command, which was using the +2 Timezone (CEST time). To fix this, I am now using UtcNow instead of Now.
This is what I am getting now:
$ echo $(($(($(date -u '+%s') - $(date -d "0001-01-01T00:00:00.0000000 UTC" '+%s'))) * 10000000 )) ; pwsh -c "[System.DateTime]::UtcNow.Ticks;"
637707132410000000
637707132415874110
As you can see, now all the digits are identical, except for the last 7th digits, since I added zeros on purpose to convert from seconds to ticks, as I am not interested in fractions of seconds (for now) and I consider them negligible.
Alternative way
Another way to make the two values identical (still excluding fractions of seconds), is to remove the -u option in the first date command in order to use the current time zone, and replace UTC with +0200 in the second date command. If I do this, I can leave Now on the PowerShell command (instead of replacing it with UtcNow).
By doing this, I am getting:
$ echo $(($(($(date '+%s') - $(date -d "0001-01-01T00:00:00.0000000 +0200" '+%s'))) * 10000000)) ; pwsh -c "[System.DateTime]::Now.Ticks;"
637707218060000000
637707218067248090
If you also want fractions of seconds
I just understood that if you also need to consider fractions of seconds, then you just need to add the result of date '+%N' (nanoseconds) divided by 100 to the calculation, in any of the two approaches shown above.
Since the result of date '+%N' can have some leading zeros, Bash may think it's an octal value. To avoid this, just prepend 10# to explicitly say it is a decimal value.
For example, taking the second approach shown above (the "alternative way"), now I get:
$ echo $(($(($(date '+%s') - $(date -d "0001-01-01T00:00:00.0000000 +0200" '+%s'))) * 10000000 + $((10#$(date '+%N')/100)) ))
637707225953311420

How to subtract 60 minutes from current time in unix

I'm currently creating a shell script that will run a python code once an hour that collects, processes, and displays data from a radar for the previous hour.
The python code I am using requires a UTC begin time and end time in format "YYYYMMDDHHmm". So far, I have found using the unix command date -u +"%Y%m%d%H%M" will retrieve my current time in the correct format, but I have not been able to find a way to subtract 60 minutes from this first time and have it output the "start" time/
code I have tried:
date +"%Y%m%d%H%M-60" >> out: 201908201833-60
now= date -u +"%Y%m%d%H%M" >> out:201908201834
echo "$now - 60" >> out: - 60
I'm just starting to self teach/learn shell coding and I am more comfortable with python coding which is why my attempts are set up more like how you would write with python. I'm sure there is a way to store the variable and have it subtract 60 from the end time, but I have not been able to find a good online source for this (both on here and via Google).
You can use -d option in date:
date -u +"%Y%m%d%H%M" -d '-60 minutes'
or else subtract 1 hour instead of 60 minutes:
date -u +"%Y%m%d%H%M" -d '-1 hour'
To be able to capture this value in a variable, use command substitution:
now=$(date -u +"%Y%m%d%H%M" -d '-1 hour')
On OSX (BSD) use this date command as -d is not supported:
now=$(date -u -v-1H +"%Y%m%d%H%M")
Your current attempt has some simple shell script errors.
now= date -u +"%Y%m%d%H%M" >> out:201908201834
This assigns an empty string to the variable now and then runs the date command as previously. If the plan is to capture the output to the variable now, the syntax for that is
now=$(date -u +"%Y%m%d%H%M")
Next up, you try to
echo "$now - 60"
which of course will output the literal string
201908201834 - 60
rather than perform arithmetic evaluation. You can say
echo "$((now - 60))"
to subtract 60 from the value and echo that -- but of course, date arithmetic isn't that simple; subtracting 60 from 201908210012 will not produce 201908202312 like you would hope.
If you have GNU date (that's a big if if you really want to target any Unix) you could simply have done
date -u -d "60 minutes ago" +%F%H%M
but if you are doing this from Python anyway, performing the date extraction and manipulation in Python too will be a lot more efficient as well as more portable.
from datetime import datetime, timedelta
dt = datetime.strptime(when,'%Y%m%d%H%M')
print(dt - timedelta(minutes=60))
The shell command substitution $(command) and arithmetic evaluation $((expression)) syntaxes look vaguely similar, but are really unrelated. Both of them have been introduced after the fundamental shell syntax was already stable, so they had to find a way to introduce new syntax which didn't already have a well-established meaning in the original Bourne shell.

Using a different date in a shell script

SO, I have a whole script that runs every hour with a date as input. Normally, it takes the current time, but now I need it to run for an interval of time in the past, every hour as well.
What I've done so far is:
DEFINING THE OLD DATE
8 start_date=20131218
9 num_hours=5
10 for i in `seq 1 $num_hours`
11 do
12 date=`date -d "${start_date}+${i} hours"`
13 echo $date # Use this however you want!
14
.
.
.
25 done
The starting date is Dec 18, 2013 and then in each iteration it should give me one more hours from the starting time. This part I found it in another article here and it works. The problem comes when I do
echo $(date)
it prints the current time instead of the time that I previously defined. Of course any other variable that I define from the date has the values from the current time. For instance,
18 datestamp=$(date +%F)
19 hourstamp=$(date +%H)
I'm new in shell programming and I have no idea what to do. Any help?
Thanks in advance.
What you want is this:
18 datestamp=$(date -d "${start_date}+${i} hours" +%F)
19 hourstamp=$(date -d "${start_date}+${i} hours" +%H)
As #BMW said, try to avoid use the date as a variable name to avoid ambiguity.
$(date) will run the command date and export the result.
so when echo it, it will return current date/time.
Second, date is the unix command, avoid to use it as a variable. so this will fix your issue"
DATE=`date -d "${start_date}+${i} hours"`
echo $DATE

Time condition loop in shell

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

Custom format for time command

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.

Resources