Bash Comparing time using date - bash

I have a script that runs in the background and has a bunch of loops in it that check stuff and do things based on those checks. As it's right now I have a big main loop that runs every 60 seconds and smaller loops that constantly do a set of commands, sleep for an interval and then loop again.
The Interval variable is in seconds, but that could be changed to minutes or hours.
The code as it's now:
Small_Loop () {
while :; do
do things
sleep $Interval
done
}
Main_Loop () {
while :; do
test stuff and call functions based on those tests
sleep 60
done
}
All the small loops get called with a "&" after them and lastly the Main loop gets called normally.
As this is really ugly and resource heavy, how could I do this using date comparisons?
It would get the time in military format, 12:00, add the interval to that, (so if the interval is one hour it would be 13:00) and the Main_Loop could simply compare those while it loops until it needs to do something.
Something like this:
Update_Interval () {
#get the new interval in **:** format
}
Main_Loop () {
while :; do
if [ "`date +%R`" = "$Interval" ]; then
#do the Small_Loop's job
fi
Update_Interval
done
}
So I guess the real question is: How to run a block of command every set interval using date comparisons.
I found out that I could use watch, but can that be used inside a script without it interfering with stuff?

I wouldn't use "+%R" as simply using "+%s" would be significantly simpler.
prevtime=`date +%s`
currtime=0
#Interval in which the block is executed
interval=5
#The block to execute
function block() {
echo -e "\a" #plays a beep noise
}
function main() {
clear
echo "Running..."
sleep 0.1
currtime=`date +%s`
if [ $currtime -eq $((prevtime+interval)) ]; then
block
prevtime=$currtime
fi
}
while :; do main; done
The command "date +%s" returns the amount of "seconds since 1970-01-01 00:00:00 UTC". This means your number will always be in seconds and the change in time will always be a positive number towards the future and a negative number towards the past. So all you really need to do is check how much the number has changed. If it has changed by 5, you know 5 seconds have passed.
If you want more precise timing, you can also figure out how many nano seconds have past rather than seconds using "+%s%N".
You don't have to run any background processes just as long as the block of code can execute within the interval time. If the interval time is, in your case, 60 seconds, and the block of code takes 65 seconds to execute, then it will not execute the block of code for the next interval.
As others have pointed out, you can also use cron.

Related

Go lang, don't understand what this code does

I am noob in golang, but I would like to change a source code that writes data into database every minute to every second. I have trobles to find what Tick does in the code. The config.SampleRate is integer = 1, which means every minute = every 60 seconds
What this tick is all about and the end part of it: <-tick, combined with counter i?
i := 0
tick := time.Tick(time.Duration(1000/config.Samplerate) * time.Millisecond)
for {
// Restart the accumulator loop every 60 seconds.
if i > (60*config.Samplerate - 1) {
i = 0
//some code here
}
//some code there
}
<-tick
i++
tick is a channel in Go. If you look at the docs, tick should send something to the channel once each time interval, which is specified by time.Duration(1000/config.Samplerate) * time.Millisecond in your code. <-tick just waits for that time interval to pass.
i keeps track of how many seconds pass, so every time it ticks, you add one to i. The if statement checks when one minute passes.
So, the code inside the if statement fires every 60 seconds, while the code right under the if block fires every second.

Vbscript check hour difference within a loop

I want to check the time difference of 1 hour within a loop. A Do-loop will be running and every single time it will see if the difference of current time and previous time period is 1 hour .
I wrote this , but i guess this is way complicated
Dim ti_me
Public Function storetime(arg1,arg2)
da_te = Time
If (DateDiff("h",da_te,arg1)=1 Or -1) Then
WScript.Echo DateDiff("h",da_te,arg1)&" hour"
End If
ti_me = arg2
End Function
Call storetime("1:18:22 PM",Time)
Do
a = Time
Call storetime(a,"")
WScript.Sleep 2000
Loop
Please Correct me .

How to choose a random time once per hour

Suppose I want to run a task once per hour, but at a variable time during the hour. It doesn't have to be truly random; I just don't want to do it at the top of the hour every hour, for example. And I want to do it once per hour only.
This eliminates several obvious approaches, such as sleeping a random amount of time between 30 and 90 minutes, then sleeping again. It would be possible (and pretty likely) for the task to run several times in a row with a sleep of little more than 30 minutes.
The approach I'm thinking about looks like this: every hour, hash the Unix timestamp of the hour, and mod the result by 3600. Add the result to the Unix timestamp of the hour, and that's the moment when the task should run. In pseudocode:
while now = clock.tick; do
// now = a unix timestamp
hour = now - now % 3600;
hash = md5sum(hour);
the_time = hour + hash % 3600;
if now == the_time; then
do_the_work();
end
end
I'm sure this will meet my requirements, but I thought it would be fun to throw this question out and see what ideas other people have!
For the next hour to do work in, just pick a random minute within that hour.
That is, pick a random time for the next interval to do work in; this might be the same interval (hour) as the current interval (hour) if work has carried over from the previous interval.
The "time to sleep" is simply the time until then. This could also be execute "immediately" on a carry-over situation if the random time was before now: this will ensure that a random time is picked each hour, unless work takes more than an hour.
Don't make it more complex than it has to be - there is no reason to hash or otherwise muck with random here. This is how "Enterprise" solutions like SharePoint Timers (with an Hourly Schedule) work.
Schedule your task (with cron or the like) to run at the top of every hour.
At the beginning of your task, sleep for a random amount of time, from 0 to (60 - (the estimated running time of your task + a fudge factor)) minutes.
If you don't want your task to run twice simultaneously, you can use a pid file. The task can check - after sleeping - for this file and wait for the currently running task to finish before starting again.
I've deployed my suggested solution and it is working very well. For example, once per minute I sample some information from a process I'm monitoring, but I do it at variable times during the minute. I created a method of a Timestamp type, called RandomlyWithin, as follows, in Go code:
func (t Timestamp) RandomlyWithin(dur Timestamp, entropy ...uint32) Timestamp {
intervalStart := t - t % dur
toHash := uint32(intervalStart)
if len(entropy) > 0 {
toHash += entropy[0]
}
md5hasher.Reset()
md5hasher.Write([]byte{
uint8(toHash >> 24 & 255),
uint8(toHash >> 16 & 255),
uint8(toHash >> 8 & 255),
uint8(toHash & 255)})
randomNum := binary.BigEndian.Uint32(md5hasher.Sum(nil)[0:4])
result := intervalStart + Timestamp(randomNum)%dur
return result
}

Calculating IDs for model runs

I'm running some array jobs on a PBS system (although hopefully no knowledge of PBS systems is needed to answer my question!). I've got 24 runs, but I want to split them up into 5 sub-jobs each, so I need to run my script 120 times.
After giving the PBS option of -t 1-120, I can get the current job-array ID using $PBS_ARRAYID. However, I want to create some output files. It would be best if these output files used the ID that it would have had if there were only 24 runs, together with a sub-run identifier (e.g. output-1a.txt, output-1b.txt ... output-1e.txt, output-2a.txt).
What I therefore need is a way of calculating a way to get the ID (in the range 1-24) together with the sub-run identifier (presumably in a set of if-statements), which can be used in a shell-script. Unfortunately, neither my maths nor my Unix knowledge is quite good enough to figure this out. I assume that I'll need something to do with the quotient/remainder based on the current $PBS_ARRAYID relative to either 120 or 24, but that's as far as I've got...
You just need a little modular division. A quick simulation of this in Ruby would be:
p = Array.new;
(1..120).each {|i| p[i] = "Run #{1+(i/5)}-#{((i%5)+96).chr}" }
What this says is simply that the run should start at 1 and increment after each new section of five, and that the trailing sub-run should be the ascii character represented by 96 plus the position of the sub-run (eg, 97 == 'a').
Here it is in Bash:
#!/bin/bash
chr() {
local tmp
[ ${1} -lt 256 ] || return 1
printf -v tmp '%03o' "$1"
printf \\"$tmp"
}
for ((i = 0; i < ${#ARP[*]}; i++))
do
charcode=$((($i % 5)+97))
charachter=$(chr "$charcode")
echo "Filename: output-$((($i/5)+1))$charachter"
done
I just used ARP as the name of the array, but you can obviously substitute that. Good luck!

In Ruby, how do I combine sleep with gets? I want to wait for user response for 1 min, otherwise continue

I'm running a loop, in which I wait for a user response using the "gets.chomp" command.
How can I combine that with a sleep/timer command?
For example. I want it to wait 1 min for the user to enter a word, otherwise it would continue back to the loop.
You should look at Ruby's Timeout.
From the docs:
require 'timeout'
status = Timeout::timeout(5) {
# Something that should be interrupted if it takes too much time...
}
I think the Timeout method above is probably the most elegant way of solving this problem. Another solution that is available in most languages is using select. You pass a list of file descriptors to monitor and an optional timeout. The code is much less concise:
ready_fds = select [ $stdin ], [], [], 10
puts ready_fds.first.first.gets unless ready_fds.nil?
How about:
def gets_or_timeout(to)
# Use thread and time limit to wait for a key or refresh after time if no key is hit.
t=Thread.new{ print "\n> "; gets}
t.join(to) #wait for end or number of seconds
t.kill
end
...
gets_or_timeout(60)
...

Resources