Time-based while loop - powershell

I've been trying to get the while & the If loop to read in the time correctly, but it doesn't end the program when the right time has hit. I tried using single quotes '' and double quotes "", as well as different syntax like (-eq, -match, -ne) to see if any of those work....and they don't.
Program Goal: loops until it hits 07:00am
# While the value is 1.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
# Get's the time in 24hr format
$time = get-date -Format HH:mm:ss
# For Debugging; Writes out Time
Write-Output $time
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Completed",0,"Done",0x1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 4
# Sends the Enter Keystroke.
$wshell.sendkeys('~')
# Causes the Program to wait to send the Enter Keystroke in seconds (900sec = 15 Minutes).
Sleep 4
# If Condition; If the time is over 2am then the program quits.
If ($time -eq "02:03:00")
{
# While Loop End Condition
$value = 2
# "Debugging Output"
Write-Output $value
Write-Output $time
}
Else
{
# While Loop Condition
$value = 1
# "Debugging Output"
Write-Output $value
Write-Output $time
}
}
# "Debugging Output"
Write-Output "End"
Write-Output $time
Write-Output $value

The chances are really low that your if-statement will become true. Because your while loop takes at least 8 (2x Start-Sleep and other work) seconds until a new beginning. That means the $time variable will probably never be exactly 02:03:00. In this case I would not go for the exact time. Instead I would check if it's 02:03:00 or later. Try that:
$time = Get-Date
if ($time -ge (Get-Date -Hour 02 -Minute 03 -Second 00))
{
}

This condition should do the work:
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# While Loop End Condition
$value = 2
# more actions
}
It's comparing the current time with the DateTime object with current day, but time set to 07:00:00.
Keep in mind two things:
It will allow the loop to run only between midnight and 7AM. If you want to start the script the day before you need to adjust the conditions.
It might be more readable to not use if, but put the condition directly in while() like this:
while ((Get-Date) -lt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# do something
}
Currently, you're checking for exact time, so in theory the end condition might be met, However, if it hits that specific line one second before/after, it won't stop the loop.

Finished Code (at least this version of it anyhow). Appreciate the help too :)!
<###
Program Goal: Prevent PC Timeout/Sleep; also loops until it hits 07:00am
Date: 10/14/19
Version: 1.2
###>
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
# Get's the time in 24hr format
$time = get-date
# While the value is not equal to 2.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
$wshell.Popup("Operation Completed", 5,"Done", 1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 2
# If Condition; If the time is over 7am then the program quits.
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0))
{
# While Loop End Condition
$value = 2
}
else
{
# While Loop Condition
$value = 1
# Causes the Program to wait (900sec = 15 Minutes) to prevent PC Timeout, will re-run again after Sleep.
Sleep 900
}
}
# Cleans up the COM Object
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshell) | Out-Null

Related

Adding Time out section in powershell script

I am trying to test Time out after 30 seconds.
Sample code:
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Seconds 30
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$stopwatch.Start()
$timeout.Seconds
$stopwatch.elapsed.Seconds
do{
if($a -eq "n"){
Write-Host "This block will never run"
break
}
if($stopwatch.elapsed.Seconds -lt $timeout.Seconds){
Write-Host "Testing this block: Time OUT!!"
break
}
}while($a -eq $b)
$stopwatch.Stop()
But the if block if($stopwatch.elapsed.Seconds -lt $timeout.Seconds) is true even $stopwatch.elapsed.Seconds value is 0 and $timeout.Seconds value is 30 in the loop and complete the code in few milliseconds and not taking 30 seconds to print the Time out statement.
Could anyone please give me pointer to resolve this issue.
A couple of things:
You don't need these two lines: $timeout.Seconds and $stopwatch.elapsed.Seconds above the loop
Your while condition should be while($a -ne $b)
The test inside the loop should read if($stopwatch.elapsed.Seconds -ge $timeout.Seconds)
Try
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Seconds 30
$stopwatch = [System.Diagnostics.Stopwatch]::new()
$stopwatch.Start()
do {
if($stopwatch.Elapsed.Seconds -ge $timeout.Seconds){
Write-Host "Testing this block: Time OUT!!"
break
}
# no timeout, so proceed with what needs to be done here
# . . .
} while($a -ne $b) # loop forever unless you set $a equal to $b in the loop somewhere
$stopwatch.Stop()
Theo's helpful answer addresses incidental logic problems with your approach and offers a solution that probably will work, but isn't fully robust: If the activity in your loop exceeds 1 minute before the timeout condition is tested, the test won't work as intended (even with the logic problems fixed).
You have two options:
Use .TotalSeconds instead of .Seconds, for the reasons explained below.
More simply, taking advantage of the fact that [timespan] instances are directly comparable (see below), you can use:
if ($stopwatch.elapsed -gt $timeout) { # ...
As zett42 points out, [timespan] instances are directly comparable, due to implementing the .NET System.IComparable interface (as well as its generic counterpart); e.g.:
# -> $true - a timespan representing a 61-second duration
# is greater than one representing a 60-second (1-minute) duration.
[timespan]::FromSeconds(61) -gt [timespan] '00:01:00'
Therefore, as shown in the top section, you can simply directly compare $stopwatch.elapsed and $timeout - both of which are [timespan] instances.
The .Seconds property of a [timespan] instance is only the seconds component, potentially alongside larger units, such as minutes (.Minutes) and hours (.Hours)
You need the .TotalSeconds property to get the total amount of seconds (analogously, there are also .TotalDays, .TotalHours, and .TotalMinutes properties).
Also note that .Seconds is always a whole number ([int]), whereas .TotalSeconds can be a fractional value ([double]).
To illustrate the difference:
PS> [timespan] '00:01:05' | # 1 minute and 5 seconds
Select-Object Seconds, TotalSeconds
Seconds TotalSeconds
------- ------------
5 65
#sivam The issue is-
You're not applying the proper properties of the timespan command if it goes beyond 59 seconds then at 60 seconds it will consider it 1 minute.
Update the condition inside the loop if($stopwatch.elapsed.Seconds -lt $timeout.Seconds)
Try
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Minutes 1
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$stopwatch.Start()
$timeout.Minutes
$stopwatch.elapsed.Minutes
do{
if($a -eq "n"){
Write-Host "This block will never run"
break
}
if($stopwatch.elapsed.Minutes -ge $timeout.Minutes){
Write-Host "Time OUT!!"
break
}
}while($a -ne $b)
$stopwatch.Stop()

Powershell Timed Function not returning correct time

I am writing a safety function for my powershell script so they do not run during working hours in case of disruptions. This means that I can automate more as I do not have to worry about the script taking too long as it will pause when needed then resume.
Unfortunately, when it is supposed to be running it is still sleeping. Any pointers on what I am missing would be appreciated.
Function WorkingHours {
$WorkingHours = "20:00-7:00"
if ($WorkingHours -match '^[0-9]{1,2}:[0-5][0-9]-[0-9]{1,2}:[0-5][0-9]$') {
$current = Get-Date
$start = Get-Date ($WorkingHours.split("-")[0])
$end = Get-Date ($WorkingHours.split("-")[1])
# correct for hours that span overnight
if (($end - $start).hours -lt 0) {
$start = $start.AddDays(-1)
}
# if the current time is past the start time
$startCheck = $current -ge $start
# if the current time is less than the end time
$endCheck = $current -le $end
# if the current time falls outside the window
if ((-not $startCheck) -or (-not $endCheck)) {
# sleep until the operational window starts again
$sleepSeconds = ($start - $current).TotalSeconds
Write-Host 'Starting to Sleep for'$sleepSeconds
if ($sleepSeconds -lt 0) {
# correct for hours that span overnight
$sleepSeconds = ($start.addDays(1) - $current).TotalSeconds
}
# sleep until the wake up interval
Start-Sleep -Seconds $sleepSeconds
}
}
}

Powershell do while loop script output unexpected

Trying to get the active processes for powershell(example) after every 5 seconds. Running the below script. I killed 2 powershell sessions and the script which is running every 5 seconds doesn't update the active sessions as 3 instead it displays as 5 sessions. please help me where am going wrong
$process = Get-Process powershell*
$count = $process.count
Do {
$count
sleep -Seconds 5
} until ($count -eq 1)
Output:
You just need to put your first two statements inside your do block.
do
{
$process = Get-Process powershell*
$count = $process.count
$count
sleep -Seconds 5
} until ($count -eq 1)
that way you recalculate $count each time you loop, otherwise the value never changes as you observed.

Infinite loop in comparing Time Powershell

I've been trying to create code in PowerShell that's gonna run if the time is below a specific time but upon experimenting it always gets stuck in an infinite loop executing even when the condition is already not satisfied
here's my code:
$a = Get-Date
Write-Host $a
[datetime]$time = "05/12/2016 1:57:00 AM"
DO
{
if($a -gt $time) {break}
Write-Host "not yet time"
if($a -gt $time) {break}
}while( $a -le $time)
CLS
Write-Host "done"
But it still is running even at 1:59 AM already. Any ideas on how to properly compare time? I want it to run till a specific time or I want it to run at least 2 hours.
Since you only assign the output from Get-Date to $a once, it's never updated, and you're performing the exact same comparison (times 3) every time the loop runs.
Call Get-Date in the loop body or conditional to get the updated time:
[datetime]$time = "05/12/2016 1:57:00 AM"
do {
Write-Host "not yet time"
}while($(Get-Date) -le $time)
cls
Write-Host "done"

Synchronizing pc-clock occasionally by Powershell and W32tm if time difference -gt 10 sec?

once and a while my prg running at a quite high cpu-time consumption.
After that the pc-clock could differ up to 10 sec despite I sync. the pc-clock with the internet-time twice a day.
Now I want to sync. the pc-clock if the time differs more than (e.g.) 2 sec. and I'd like to use:
PS C:\Windows\system32> w32tm /stripchart /computer:us.pool.ntp.org /dataonly /samples:5
Tracking us.pool.ntp.org [50.7.72.4:123].
Collecting 5 samples.
The current time is 08.08.2014 08:35:44.
08:35:44, -00.1019929s
08:35:47, -00.1082726s
08:35:49, -00.1077418s
08:35:51, -00.1082350s
08:35:53, -00.1007278s
My problem how can my powershell script can receive the time differences from the w32tm?
Then of course I can check the difference and do:
start-process w32tm /resync
For a millisecond the console appears but for sure the resync must fail as I do not start w32tm as admin yet - which causes an access not allowed-error.
Thanks in advance!
Fooling around I fond the solution - surprisingly easy:
$x = w32tm /stripchart /computer:us.pool.ntp.org /dataonly /samples:5 | Out-String
$x
As I cannot answer my own question here now my solution.
Once and a while high cpu-usages causes the pc-clock to run too fast and I have time sensitive programs that need to know the exact time.
On the other hand I can't define the exact time-stamp(s) of Win 7's time-sync (only every x seconds) which might cause a time change right when I do not want it.
So I programmed this solution which is fine for me but can be improved as
1) the time of the check and sync. are hard coded: always at Min 12 or 42 and Sec. 27 and
2) it must run as Administrator as other wise the time-sync fails with Error 0x80070005 => access denied.
Anyway for those who might be interested:
# http://technet.microsoft.com/en-us/library/cc773263%28v=ws.10%29.aspx
function chkSync { param([bool]$sync=$true)
$x = w32tm /stripchart /computer:ptbtime2.ptb.de /dataonly /samples:5 | Out-String
$y = $x.Split("`n")
$c = 0
$n = 0
foreach( $a in $y ) {
$z = $a.Split(",")
if ( $z[1] -match '([\d\.\-\+]+)' ){
$b = [double]$matches[1]
$c += $b; $n++
}
}
if ( $n -gt 0 ) {
$d = $c/$n
} else { $d = 0 }
if ( $sync -and $d*$d -gt 1.0 ) { # instead of [Math]::Abs
Write-Host (get-date).ToString('HH:mm:ss') "bef. sync.:" $d.ToString('f3') "start sync. now"
$s = w32tm /resync | Out-String
Write-Host $s # Error 0x80070005 => access denied: run as adimnistartor!!
chkSync $false
} else {
# every 30 min at 12:47 -or 42:47
$slp = [int]$(1800 + 747 - ((((get-date).Minute)%30)*60 + $((get-date).Second)))
Write-Host (get-date).ToString('HH:mm:ss') "avg. delay:" $d.ToString('f3')`
"next check:" $($(get-date).AddSeconds($slp)).ToString('HH:mm:ss')
}
Start-Sleep -s $slp
}
do { chkSync } while ( $true )