Timed loop that restarts if criteria is met or time expires? - powershell

I am trying to run through a loop every 3 seconds for xx minutes. Inside the while loop is an IF statement. If the statement is true, then fire off my command and restart the loop for the predefined number of minutes again.
If the if statement is never true after the minutes defined, fire off my command and then restart the loop all over again with the time set above.
I could do this in a batch file with goto but cannot figure out how to do this in PowerShell.
Here is a representation of what I want to accomplish.
$minutes = 5
while(loop for 5 minutes)
{
if(1 -eq 1)
{
do something
restart the while loop for the minutes defined above
}
start-sleep -seconds 3
}
do something here
restart the while loop for the minutes defined above
Update:
Here is what I came up with. This is my first time trying to write a script in PowerShell so I am almost certain there is a more elegant way to write this.
# we need a constant loop going
while(1 -eq 1)
{
# now we need our timed loop. set the timer -seconds 3 (3 seconds right now for testing)
$timeout = new-timespan -seconds 3
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout)
{
# check to see if things are still true
if($something -eq "true")
{
echo "Do nothing."
}
else
{
echo "Do something and restart."
# break out of this timed loop since we want to restart it
break
}
# check every 1 second
start-sleep -seconds 1
}
echo "$something did not equal true in the IF above or the timer has run out. Do something and restart."
# continue restarts the loop
continue
}

Shouldn't you be able to just reset $sw?
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout) {
if ($Condition) {
$sw.Reset()
}
}

Related

Custom sleep function to sleep UI while not hanging it - Problem: The timer drifts slightly

I've written a custom Start-SleepNoUIHang function to sleep a windows form UI whilst not hanging it to allow for user interaction using a ForEach loop and inside that loop it calls [System.Windows.Forms.Application]::DoEvents() to prevent it from doing so.
It works as I intended but the only trouble is that the function slightly drifts past the argument $Milliseconds.
If I set that to say 5000 the timer takes around 6300 milliseconds.
I've tried to add a counter inside the ForEach loop and then break out of it once it reaches the $Milliseconds argument but that doesn't seem to work.
I didn't want to use the .net timer so I created this as a one-liner to use anywhere in the program where it was needed.
Any help here would be greatly appreciated.
Here's the code (with comments):
<#
This function attempts to pause the UI without hanging it without the need for a
timer event that does work.
The only trouble is that the timer slight drifts more than the provided
$Milliseconds argument.
#>
function Start-SleepNoUIHang {
Param
(
[Parameter(Mandatory = $false, HelpMessage = 'The time to wait in milliseconds.')]
[int]$Milliseconds
)
$timeBetween = 50 # This value seems to be a good value in order for the UI not to hang itself.
$timeElapsed = 0 # Increment this to check and break out of the ForEach loop.
# ($Milliseconds/$timeBetween)*$timeBetween # Time is the total wait time in milliseconds.
1..($Milliseconds/$timeBetween) | ForEach {
Start-Sleep -Milliseconds $timeBetween
Try { [System.Windows.Forms.Application]::DoEvents() } catch{} # A try catch here in case there's no windows form.
$timeElapsed = $timeElapsed + $timeBetween # Increment the $timeElapsed counter.
Write-Host $timeElapsed
# This doesn't seem to have any effect on the timer. It ends on its own accord.
if ($timeElapsed -gt $Milliseconds) {
Write-Host 'Break'
break
}
}
}
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
Write-Host "Started at $(get-date)"
Start-SleepNoUIHang -Milliseconds 5000
Write-Host "Ended at $(get-date)"
Write-Host "Total Elapsed Time: $($elapsed.Elapsed.ToString())"
I've also tried to do a While loop replacing the ForEach loop with this but that behaved the same.
While ( $Milliseconds -gt $timeElapsed ) {
$timeElapsed = $timeElapsed + $timeBetween # Increment the $timeElapsed counter.
Start-Sleep -Milliseconds $timeBetween
Write-Host $timeElapsed
}

To check a windows service state as running by running a loop upto 3 increments

Hi I have a scenario where I need to to check a windows service state as running by running a loop upto 3 increments and after every increment wait for 10 sec before incrementing the loop.How to achieve this?
Perhaps using a simple for() loop like this:
$serviceName = 'TheServiceName'
$maxTries = 3
for ($i = 0; $i -lt $maxTries; $i++) {
$status = (Get-Service -Name $serviceName).Status
Write-Host "Service '$serviceName' status is $status"
if ($status -eq 'Running') {
# exit the loop
break
}
# service is not running, so sleep for 10 seconds and try again
Start-Sleep -Seconds 10
}
Make sure you test on the service Name or DisplayName property, they differ..

Powershell Script to Kill all PIDs that are non-responsive for 3 minutes

I need some help with a powershell script to kill all PIDs that are non-responsive for 3 minutes.
This is my script, but is not doing the trick. This script is running, but i need it to run as in a while loop, forever since the computer is running till the end of the working time.
I need to have a list with all the processes that are unresponsive for a period of 3 minutes. After 3 minutes, if those processes from the list have the same status -eq NoT Responsing to kill them. I don't want to kill the processes that are not responsing for 5 seconds or so, only those that are hanging for more than 3 minutes.
My purpose is to kill the PIDs that are running with the status Not Responding for more than 3 minutes.
As you know, processes sometimes are unresponsive for a couple of seconds e.g IE hangs for 7 seconds till the server response with the DOM etc. hence I need to close all the pids that are hanging with the status Not Responsive for more than 3 min.
while (1) {
# if ( $allProcesses = get-process -name $pN -errorAction SilentlyContinue ) {
foreach ($oneProcess in $allProcesses) {
if ( -not $oneProcess.Responding ) {
write "Status = Not Responding: Kill& Restart.."
$oneProcess.kill()
## restart ..
} else {
write "Status = either normal or not detectable (no Window-Handle)"
}
}
start-sleep 5
}
A quick and dirty solution, not tested, is based on idea about storing the process info in a hashtable and performing a re-check after sleep period. Like so,
while($true){
# Get a list of non-responding processes
$ps = get-process | ? { $_.responding -eq $false }
$ht = #{}
# Store process info in a hash table.
foreach($p in $ps) {
$o = new-object psobject -Property #{ "name"=$p.name; "status"=$p.responding; "time"=get-date; "pid"=$p.id }
$ht.Add($o.pid, $o)
}
# sleep for a while
start-sleep -minutes 3
# Get a list of non-responding processes, again
$ps = get-process | ? { $_.responding -eq $false }
foreach($p in $ps) {
# Check if process already is in the hash table
if($ht.ContainsKey($p.id)) {
# Calculate time difference, in minutes for
# process' start time and current time
# If start time's older than 3 minutes, kill it
if( ((get-date)-$ht[$p.id].Time).TotalMinutes -ge 3 ) {
# Actuall killing
$p.kill()
}
}
}
}
It's certainly possible to store process objects in the hashtable, but in most cases all you need is process id. Mind that process ids are recycled. If you are spawning a lot of processes, it might be reasonable to check $p.time value so that newly created process isn't killed instead.

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.

Time conditioned 'For' loop in Powershell

What I am looking for is a Powershell version of this: Time condition loop in shell
So I can have something like this:
if(Condition -eq True){
for(3000){ #I assume it will use milliseconds
COMMAND
}
}
EDIT: This will not be running continuously, unlike the example, it could be triggered at any time, and when the time ends, it should exit out of the loop and resume the program as normal.
This is one way, with some simple 100ms throttling:
$sw = [Diagnostics.Stopwatch]::StartNew()
while ($sw.ElapsedMilliseconds -lt 3000)
{
#COMMAND HERE
Write-Host "ElapsedMilliseconds $($sw.ElapsedMilliseconds)"
Start-Sleep -Milliseconds 100
}
$sw.Stop()