PowerShell - Make a countdown timer in a looping script [duplicate] - windows

I have a script of counter that expects two parameters:
1) seconds to wait before the counter starts.
2) counter duration in seconds.
For example, if I input 3,10
I would like that after 3 seconds the timer will countdown from 10 to 0 and write it to the output every second.
This is my script:
$timeBeforeStart = $args[0]
$waitSeconds = $args[1]
$startTime = get-date
$endTime = $startTime.addSeconds($waitSeconds)
$timeSpan = new-timespan $startTime $endTime
start-sleep -s $timeBeforeStart
while ($timeSpan -gt 0)
{
$timeSpan = new-timespan $(get-date) $endTime
write-host $([string]::Format("`rTime Remaining: {0:d2}:{1:d2}:{2:d2}",
$timeSpan.hours, $timeSpan.minutes, $timeSpan.seconds))
sleep 1
}
Unfortunately it doesn't work, the sleep seems to work simultaneity with the counter instead of delay the counter.
PS C:\> c:\555.ps1 3 10
Time Remaining: 00:00:07
Time Remaining: 00:00:05
Time Remaining: 00:00:04
Time Remaining: 00:00:03
Time Remaining: 00:00:02
Time Remaining: 00:00:01
Time Remaining: 00:00:00
Time Remaining: 00:00:00
I've also tried start-sleep -s and the results where the same.
by the way, what is the difference between sleep and "start-sleep -s" ?

what is the difference between sleep and "start-sleep -s"?
No difference, sleep is just alias of Start-Sleep.
Unfortunately it doesn't work, the sleep seems to work simultaneity with the counter instead of delay the counter.
You might want to put the sleep before the counter starts before getting the first $timeSpan:
start-sleep -s $timeBeforeStart
$startTime = get-date
$endTime = $startTime.addSeconds($waitSeconds)
$timeSpan = new-timespan $startTime $endTime
while ($timeSpan -gt 0)
{
# ...
}

Try this way:
$timeBeforeStart = 3
$waitSeconds = 10
Start-Sleep -Seconds $timeBeforeStart
$waitSeconds..0 | Foreach-Object {
Write-Host "Time Remaining: $_"
Start-Sleep -Seconds 1
}
Time Remaining: 10
Time Remaining: 9
Time Remaining: 8
Time Remaining: 7
Time Remaining: 6
Time Remaining: 5
Time Remaining: 4
Time Remaining: 3
Time Remaining: 2
Time Remaining: 1
Time Remaining: 0

Try this way:
$timeBeforeStart = 0
$waitSeconds = 60
Start-Sleep -Seconds $timeBeforeStart
$waitSeconds..0 | Foreach-Object {
Write-Host "`r Time Remaining: $_ " -NoNewline -foregroundcolor green
Start-Sleep -Seconds 1
}

This also does the same thing:
$timeBeforeStart = 3
$waitSeconds = 10
Start-Sleep -Seconds $timeBeforeStart
$endTime = (get-date).addSeconds($waitSeconds)
while ( (get-date) -lt $endTime )
{
Write-Host "Time Remaining: $("{0}" -f ([int](new-timespan $(get-date) $endTime).totalseconds))"
Start-Sleep -Seconds 1
}

Related

Kill the process if start time is less than 2 hours

I need to kill the process if start time is less than 2 hours.
I have written the below cmdlet to find out the starttime but how to find out if is it less than 2 hours:
get-process process1 | select starttime
There is also a possibility that on some hosts process1 is not running. so I need to check first if the process1 is running
You can use a loop of your choice, in this example ForEach-Object in addition to an if condition to check if the StartTime value is lower than 2 hours.
If you need to check first is the process is running then you would need to get all processes and filter by the process name you're looking for. Then check if the returned value from Where-Object is $null or not.
$procName = 'myprocess'
$process = Get-Process | Where-Object Name -EQ $procName
if(-not $process) {
Write-Warning "$procName not found!"
}
else {
$process | ForEach-Object {
if($_.StartTime -lt [datetime]::Now.AddHours(-2)) {
try {
'Attempting to stop {0}' -f $_.Name
Stop-Process $_ -Force
'{0} successfully stopped.' -f $_.Name
}
catch {
Write-Warning $_.Exception.Message
}
}
}
}

How to keep checking the process after every 30 minutes?

I need to kill the process if start time is less than 2 hours.
I need to add sleep for 30 mins if start time is more than 2 hours.
I need to keep repeating it until the process is no more running.
I have written the below script so far to perform the above action.
$procName = 'myprocess'
$process = Get-Process | Where-Object Name -EQ $procName
if(-not $process) {
Write-Warning "$procName not found!"
}
else {
$process | ForEach-Object {
if($_.StartTime -lt [datetime]::Now.AddHours(-2)) {
Stop-Process $_ -Force
}
else {
sleep(1800)
}
}
}
}
How to add the above program in a do-while or another loop so as to keep checking until the process is no more running?
Also, how to implement a maximum timer of 4 hours?
If I understood correctly, your else condition could look like this using a do-while loop:
else {
do {
"$procName is still running, sleeping for 1800 sec"
Start-Sleep -Seconds 1800
} while(Get-Process | Where-Object Name -EQ $procName)
}
However note that this could cause an infinite loop if the process never stops or you implement a maximum timer, etc.
Following your comment regarding implementing a maximum timer, there are many ways you could do it, my personal preference would be to use a StopWatch:
else {
$timer = [System.Diagnostics.Stopwatch]::StartNew()
do {
# Do this while the process is still running AND
# the timer hasn't yet reached 4 hours
"$procName is still running, sleeping for 1800 sec"
Start-Sleep -Seconds 1800
$stillRunning = Get-Process | Where-Object Name -EQ $procName
} while($stillRunning -and $timer.Elapsed.Hours -lt 4)
$timer.Stop()
}
I suggest you use a windows schedule task that launch your powershell script every 30 minutes or so instead of blocking resource with your powershell that is waiting.
You can launch powershell and pass a script.
PowerShell.exe -File "C:\script.ps1"

Powershell script - break loop

Giving this little script in powershell:
$index = 1
$file = "C:\Users\myfile"
while ($index -le 100000)
{
$init_size = Write-Host((Get-Item $file).length/1KB)
<here is my command which populates $file>
$final_size = Write-Host((Get-Item $file).length/1KB)
$index ++
sleep 5
If ($final_size -eq $init_size) {break}
}
I don't understand why it breaks even if the init_size is different from the final_size.
Any suggestions?
Write-Host writes directly to the screen buffer and doesn't output anything, so the value of both $init_size and $final_size are effectively $null when you reach the if statement.
Do Write-Host $variable after assigning to $variable and it'll work:
$index = 1
$file = "C:\Users\myfile"
while ($index -le 100000) {
$init_size = (Get-Item $file).Length / 1KB
Write-Host $init_size
<here is my command which populates $file>
$final_size = (Get-Item $file).Length / 1KB
Write-Host $final_size
$index++
sleep 5
If ($final_size -eq $init_size) { break }
}
Calling Write-Host on the results of the assignment expression itself would work too:
Write-Host ($init_size = (Get-Item $file).Length / 1KB)

Getting output from a Start-Job invocation

I have the following code that works in that it creates multiple jobs and runs what's inside the scriptblock on all of the computers in the array ($SMSMembers). The problem is that it doesn't give any sort of meaningful output back that I can use to determine if the code ran successfully or not. I have tried about 100 different things that I have Googled but none of the solutions seemed to work for me. This is the code I'm trying to run that I thought should work according to other posts I've seen on StackOverflow.
$SMSMembers = #("computer1","computer2","computer3")
$output = #()
foreach ($compName in $SMSMembers) {
$scriptblock = {
$file = {"test"}
$filepath = "\\$using:compName\c$\scripts\NEWFILE.txt"
$file | Out-File -FilePath $filepath
Start-Sleep -Seconds 5
Remove-Item $filepath
}
$output += Start-Job -ScriptBlock $scriptblock | Get-Job | Receive-Job
}
Get-Job | Wait-Job
foreach ($item in $output) {
Write-Host $item
}
This script doesn't do much except copy a file to a remote computer and then delete it. I would just like to get output if the job was successful or not. Like I said this code works like it should, I just don't get feedback.
My end goal is to be able to send a command to an array of computers ($SMSMembers) and request the current user with this code and get the username input back:
$user = gwmi Win32_ComputerSystem -Comp $compName |
select Username -ExpandProperty Username
You create the job, get the job info, and then receive the job back to back to back, before the job can complete. Instead, collect the job info, then outside the loop wait for the jobs to finish, and receive the output when the jobs are done.
$SMSMembers = #("computer1","computer2","computer3")
$scriptblock = {
$file = {"test"}
$filepath = "\\$using:compName\c$\scripts\NEWFILE.txt"
$file | out-file -FilePath $filepath
Start-Sleep -Seconds 5
remove-item $filepath
}
$Jobs = foreach($compName in $SMSMembers){
Start-Job -ScriptBlock $scriptblock
}
Wait-Job $Jobs
$Output = Receive-Job $Jobs
foreach ($item in $output){
write-host $item
}
Edit: Modified the script slightly so I wasn't randomly copying files around, but it should still function the same. Then tested it with the expected results:
$SMSMembers = #("computer1","computer2","computer3")
$scriptblock = {
$RndDly=Get-Random -Minimum 10 -Maximum 45
start-sleep -Seconds $RndDly
"Slept $RndDly, then completed for $using:compname"
}
$Jobs = foreach($compName in $SMSMembers){
Start-Job -ScriptBlock $scriptblock
}
Wait-Job $Jobs
$Output = Receive-Job $Jobs
foreach ($item in $output){
write-host $item
}
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed True localhost ...
3 Job3 BackgroundJob Completed True localhost ...
5 Job5 BackgroundJob Completed True localhost ...
Slept 30, then completed for computer1
Slept 27, then completed for computer2
Slept 11, then completed for computer3
Look at the below code and i think you should be able to figure this out.
> Get-job | Receive-Job 2>&1 >> c:\output.log
The 2>&1 >> with collect all output from Get-Job | Receive-Job it should work similarly for start-job

Find time difference in minutes using a PowerShell script

I am trying to find the time difference between the last updated time and current time for a file. How do I extract TotalMinutes data from the output?
$Date = Get-Date
$Files = gci "C:\Users\ABCD\Documents\command.txt"
ForEach ($File in $Files){
$FileDate = $File.LastWriteTime
}
$DURATION=$Date-$FileDate
Echo $DURATION
Output is coming as below
Days : 0
Hours : 2
Minutes : 21
Seconds : 37
Milliseconds : 311
Ticks : 84973115857
TotalDays : 0.0983485137233796
TotalHours : 2.36036432936111
TotalMinutes : 141.621859761667
TotalSeconds : 8497.3115857
TotalMilliseconds : 8497311.5857
You will not need a loop, after getting a single file:
$Files = gci "C:\Users\ABCD\Documents\command.txt"
ForEach ($File in $Files){
$FileDate = $File.LastWriteTime
}
In this case, $Files might as well be $File, making the loop completely redundant:
$File = gci "C:\Users\ABCD\Documents\command.txt"
$FileDate = $File.LastWriteTime
In the exact same way you extracted LastWriteTime, you can get TotalMinutes:
$Date = Get-Date
$DURATION = $Date - $FileDate
$DURATION.TotalMinutes
Here is a complete answer:
$Date = Get-Date
$Files = gci "C:\Users\ABCD\Documents\command.txt"
ForEach ($File in $Files){
$FileDate = $File.LastWriteTime
}
$DURATION=$Date-$FileDate
Write-Host "$($DURATION.TotalMinutes)"
You can use the below command to get just TotalMinutes
$mins = $DURATION.TotalMinutes
$StartDate=(GET-DATE)
#wait a few seconds
$EndDate=(GET-DATE)
$diff = NEW-TIMESPAN -Start $StartDate -End $EndDate
Write-Output "Time difference is: $diff"
$diff
#to see minutes only
$diff.Minutes
#or seconds
$diff.Seconds
Assuming the file exists, here's a one-liner:
((Get-Date) - (Get-ChildItem 'C:\Users\ABCD\Documents\command.txt').LastWriteTime).TotalMinutes
You can either let it echo to the screen as is, or you can assign it to a variable if you want to retain it.

Resources