Time conditioned 'For' loop in Powershell - 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()

Related

Interrupting a loop

I just do my first steps with powershell and have a questions about a loop I'm using in a script:
for ($i=1; $i -le 100; $i++){
$res = Test-Connection $IP -count 1 -Quiet
...do something more
start-sleep -seconds 30
}
This script does not allow to close the windows form (it's started from a GUI) or interrupt the loop. Is there a way to do so? Sometimes I want to stop the loop manually.
Thanks a lot for your help.
I think you might want to use powershell flow control.
With powershell flow control you are able to manually control loops in powershell.
Let me give you an example:
for ($i=1; $i -le 100; $i++){
$res = Test-Connection $IP -count 1 -Quiet
...do something more
if ($res -eq $anyresultyouwouldexpect) {
break ##with **break** you interupt the loop completely- You script
## would continue after the loop.
}
}
There are also flow control statements like continue to jump into the next iteration loop. It is depending on what you need in your case.

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
}

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.

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

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()
}
}

How can you continue executing a PowerShell script, only if it has been called 3 times in 1 minute?

I have a script that is being called via a Windows Scheduled Task, and that task is triggered based on a certain Windows Application Event. It is only critical to execute the script, though, if the event occurs 3 or more times in 1 minute; if the event occurs once a minute, no action should be taken.
I know this can be handled in the script itself. Let's say there are at least 2 new variables I will need:
# time window, in seconds
$maxTime = 60
# max number of times this script needs to be called, within $maxTime window,
# before executing the rest of the script
$maxCount = 3
I started outlining an algorithm using a temp file as tracking, but thought there might be a simpler solution that someone can show me. Thanks
You could store your execution times in an environment variable.
Before this script will work, you must create the LastExecutionTimes environment variable.
$maxTime = 60
$maxCount = 3
$now = Get-Date
# Get execution times within the time limit.
$times = #($env:LastExecutionTimes -split ';'|
Where-Object {$_ -and $now.AddSeconds(-1 * $maxTime) -lt $_})
$times += '{0:yyyy-MM-dd HH:mm:ss}' -f $now
$env:LastExecutionTimes = $times -join ';'
if($times.Length -lt $maxCount) {return}
# Reset the execution times
$env:LastExecutionTimes =''
Write-Host 'Continue Script' -ForegroundColor Yellow
I would write a text file and a secondary script or function to check it. Where essentially it will call it each time, and then writes the information writes to a text file at call time.
The something like this:
if(!((Get-Date).AddMinutes(-1) -lt $oldTime))
{
$CurDate = Get-Date
"$CurDate, 1" | out-File "TheCheck.txt"
}
else
{
$counter++
if($counter -ge 3) {Call WorkerFunction}
else{
"$oldTime, $counter" | Out-File "TheCheck.txt"
}
Its missing some variables, but overall should be functional as a supplemental script. Then what your scheduled task actually does is call this, if the time since the $oldTime is over 1 minute, then it over writes the file with the current time and 1 for a $counter variable. If its less than a minute since the first call it then checks the $counter and if it is 3 or higher (could also do -eq ) to 3 then it calls your main script.