Powershell do while loop script output unexpected - powershell

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.

Related

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.

Time-based while loop

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

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.