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
}
In PowerShell, how do I exit this while loop that is nested inside a switch statement, without executing the code immediately following the while block? I can't seem to figure it out. Everything I've tried so far results in that block of code being executed.
Here's what I'm trying to accomplish:
Check for the presence of a file and notify user if file is
detected.
Check again every 10 seconds and notify user
If the file is not detected, then exit the loop and switch, and continue
with Step #2
If the file is still detected after 30 seconds, timeout and exit
the script entirely.
Here's the code:
try {
#Step 1
$Prompt = <Some Notification Dialog with two buttons>
switch ($Prompt){
'YES' {
# Display the Windows Control Panel
#Wait for user to manually uninstall an application - which removes a file from the path we will check later.
$Timeout = New-Timespan -Seconds 30
$Stopwatch = [Dispatch.Stopwatch]::StartNew()
while ($Stopwatch.elapsed -lt $Timeout) {
if (Test-Path -Path "C:\SomeFile.exe" -PathType Leaf) {
Write-Host "The file is still there, remove it!"
return
}
Start-Sleep 10
}
#After timeout is reached, notify user and exit the script
Write-Host "Timeout reached, exiting script"
Exit-Script -ExitCode $mainExitCode #Variable is declared earlier in the script
}
'NO' {
# Do something and exit script
}
}
# Step 2
# Code that does something here
# Step 3
# Code that does something here
} catch {
# Error Handling Code Here
}
You can use break with a label, to exit a specific loop (a switch statements counts as a loop), see about_break.
$a = 0
$test = 1
:test switch ($test) {
1 {
Write-Output 'Start'
while ($a -lt 100)
{
Write-Output $a
$a++
if ($a -eq 5) {
break test
}
}
Write-Output 'End'
}
}
Write-Output "EoS"
Is that what you see without the try/catch? I get an exception: Unable to find type [Dispatch.Stopwatch]. Otherwise the return works ok for me.
I think what you want is break with a label going outside the switch? Then steps 2 & 3 will run. I altered the code to make a manageable example. This is more ideal when asking a question. I don't know what exit-script is.
echo hi > file
#try {
#Step 1
$Prompt = '<Some Notification Dialog with two buttons>'
$Prompt = 'yes'
:atswitch switch ($Prompt){
'YES' {
'Display the Windows Control Panel'
#Wait for user to manually uninstall an application - which removes a file from the path we will check later.
$Timeout = New-Timespan -Seconds 30
#$Stopwatch = [Dispatch.Stopwatch]::StartNew()
while (1) {
if (Test-Path -Path "file" -PathType Leaf) {
Write-Host "The file is still there, remove it!"
break atswitch
}
Start-Sleep 10
}
#After timeout is reached, notify user and exit the script
Write-Host "Timeout reached, exiting script"
'Exit-Script -ExitCode $mainExitCode #Variable is declared earlier in the script'
}
'NO' {
'Do something and exit script'
}
}
# Step 2
'Code that does something here'
# Step 3
'Code that does something here2'
#} catch {
# 'Error Handling Code Here'
#}
In some of my scripts I have a fall-back approach for getting a PSCredential object via the Get-Credential cmdlet. This is very useful when running those scripts interactively, esp. during testing etc.
If & when I run these scripts via a task scheduler, I'd like to ensure that they don't get stuck waiting for interactive input and simply fail if they don't have the necessary credentials. Note that this should never happen if the task is set to run under the correct application account.
Does anyone know of an approach that would allow me to prompt for input with a timeout (and presumably a NULL credential object) so the script doesn't get stuck?
Happy to consider a more general case with Read-Host instead of Get-Credential.
I use something similar in some of my scripts based around a timeout on read-host, code here :
Function Read-HostTimeout {
# Description: Mimics the built-in "read-host" cmdlet but adds an expiration timer for
# receiving the input. Does not support -assecurestring
# Set parameters. Keeping the prompt mandatory
# just like the original
param(
[Parameter(Mandatory=$true,Position=1)]
[string]$prompt,
[Parameter(Mandatory=$false,Position=2)]
[int]$delayInSeconds=5,
[Parameter(Mandatory=$false,Position=3)]
[string]$defaultValue = 'n'
)
# Do the math to convert the delay given into milliseconds
# and divide by the sleep value so that the correct delay
# timer value can be set
$sleep = 250
$delay = ($delayInSeconds*1000)/$sleep
$count = 0
$charArray = New-Object System.Collections.ArrayList
Write-host -nonewline "$($prompt): "
# While loop waits for the first key to be pressed for input and
# then exits. If the timer expires it returns null
While ( (!$host.ui.rawui.KeyAvailable) -and ($count -lt $delay) ){
start-sleep -m $sleep
$count++
If ($count -eq $delay) { "`n"; return $defaultValue}
}
# Retrieve the key pressed, add it to the char array that is storing
# all keys pressed and then write it to the same line as the prompt
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp").Character
$charArray.Add($key) | out-null
# Comment this out if you want "no echo" of what the user types
Write-host -nonewline $key
# This block is where the script keeps reading for a key. Every time
# a key is pressed, it checks if it's a carriage return. If so, it exits the
# loop and returns the string. If not it stores the key pressed and
# then checks if it's a backspace and does the necessary cursor
# moving and blanking out of the backspaced character, then resumes
# writing.
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
While ($key.virtualKeyCode -ne 13) {
If ($key.virtualKeycode -eq 8) {
$charArray.Add($key.Character) | out-null
Write-host -nonewline $key.Character
$cursor = $host.ui.rawui.get_cursorPosition()
write-host -nonewline " "
$host.ui.rawui.set_cursorPosition($cursor)
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
}
Else {
$charArray.Add($key.Character) | out-null
Write-host -nonewline $key.Character
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
}
}
Write-Host ""
$finalString = -join $charArray
return $finalString
}
I'm trying to overwrite a line in PowerShell written with Write-Host (I have a process that's running in a loop and I want to show percentage updated on the screen). What I've tried to do is this:
Write-Host -NoNewline "`rWriting $outputFileName ($i/$fileCount)... $perc%"
but instead of overwriting the line it stays on the same line and appends to it.
what am I missing here?
Thanks
You cannot overwrite a line in a Powershell window. What you can do is blank the window with cls(Clear-Host):
# loop code
cls
Write-Host "`rWriting $outputFileName ($i/$fileCount)... $perc%"
# end loop
But what you should really be using is Write-Progress, a cmdlet built specifically for this purpose:
# loop code
Write-Progress -Activity "Writing $outputFileName" -PercentComplete $perc
# end loop
More on Write-Progress here: http://technet.microsoft.com/en-us/library/hh849902.aspx
As a tweak to Raf's answer above, You don't have to wipe the screen every time to update your last line.
Calling Write-Host with -NoNewLine and carriage return `r is enough.
for ($a=0; $a -le 100; $a++) {
Write-Host -NoNewLine "`r$a% complete"
Start-Sleep -Milliseconds 10
}
Write-Host #ends the line after loop
It not perfect but here is a script which has a spinning character in place. The part that lets you do this is:
$origpos = $host.UI.RawUI.CursorPosition
$origpos.Y += 1
Get the current position and save it so that we can keep referring to it. As you progress you change the $host.UI.RawUI.CursorPosition. Since it was previously saved you can reset it back $host.UI.RawUI.CursorPosition = $origpos. You should be able to experiment with that.
$scroll = "/-\|/-\|"
$idx = 0
$job = Invoke-Command -ComputerName $env:ComputerName -ScriptBlock { Start-Sleep -Seconds 10 } -AsJob
$origpos = $host.UI.RawUI.CursorPosition
$origpos.Y += 1
while (($job.State -eq "Running") -and ($job.State -ne "NotStarted"))
{
$host.UI.RawUI.CursorPosition = $origpos
Write-Host $scroll[$idx] -NoNewline
$idx++
if ($idx -ge $scroll.Length)
{
$idx = 0
}
Start-Sleep -Milliseconds 100
}
# It's over - clear the activity indicator.
$host.UI.RawUI.CursorPosition = $origpos
Write-Host 'Complete'
Remove-Variable('job')
$job = Start-Job -ScriptBlock { Start-Sleep -Seconds 10 }
while (($job.State -eq "Running") -and ($job.State -ne "NotStarted"))
{
Write-Host '.' -NoNewline
Start-Sleep -Seconds 1
}
Write-Host ""
So as log as you remember where you want to go back to then you can use this logic. This will not work properly in ISE. You can also use `b as a back space character as well.
I know, thats quite old, but i was in the same Situation und modified the Solution from Boluwade Kujero, just because writing blank lines before writing the new output may result in a "flickering" output.
So in the following function, I just do overwrite the existing line, write blanks until reaching the old cursorposition, and go back to the last character of the new line.
In addition i added an optical progressbar. Progress is calculated by the function through given Parameters:
function Write-Status
{
param([int]$Current,
[int]$Total,
[string]$Statustext,
[string]$CurStatusText,
[int]$ProgressbarLength = 35)
# Save current Cursorposition for later
[int]$XOrg = $host.UI.RawUI.CursorPosition.X
# Create Progressbar
[string]$progressbar = ""
for ($i = 0 ; $i -lt $([System.Math]::Round($(([System.Math]::Round(($($Current) / $Total) * 100, 2) * $ProgressbarLength) / 100), 0)); $i++) {
$progressbar = $progressbar + $([char]9608)
}
for ($i = 0 ; $i -lt ($ProgressbarLength - $([System.Math]::Round($(([System.Math]::Round(($($Current) / $Total) * 100, 2) * $ProgressbarLength) / 100), 0))); $i++) {
$progressbar = $progressbar + $([char]9617)
}
# Overwrite Current Line with the current Status
Write-Host -NoNewline "`r$Statustext $progressbar [$($Current.ToString("#,###").PadLeft($Total.ToString("#,###").Length)) / $($Total.ToString("#,###"))] ($($( ($Current / $Total) * 100).ToString("##0.00").PadLeft(6)) %) $CurStatusText"
# There might be old Text behing the current Currsor, so let's write some blanks to the Position of $XOrg
[int]$XNow = $host.UI.RawUI.CursorPosition.X
for ([int]$i = $XNow; $i -lt $XOrg; $i++) {
Write-Host -NoNewline " "
}
# Just for optical reasons: Go back to the last Position of current Line
for ([int]$i = $XNow; $i -lt $XOrg; $i++) {
Write-Host -NoNewline "`b"
}
}
Use the function like this:
For ([int]$i=0; $i -le 8192; $i++) {
Write-Status -Current $i -Total 8192 -Statustext "Running a long Task" -CurStatusText "Working on Position $i"
}
The result will be a running progressbar that will look like this (in a single line):
Running a long Task ██████████████████░░░░░░░░░░░░░░░░░ [4.242 /
8.192] ( 51,78 %) Working on Position 4242
Hope this will help someone else
You can use the .NET console class to do exactly what you want where you want it.
Works in console windows only and not the ISE.
cls
[Console]::SetCursorPosition(40,5)
[Console]::Write('Value of $i = ')
[Console]::SetCursorPosition(40,7)
[Console]::Write('Value of $j = ')
For ($i = 1; $i -lt 11; $i++)
{
[Console]::SetCursorPosition(57,5)
[Console]::Write($i)
for ($j = 1; $j -lt 11; $j++)
{
[Console]::SetCursorPosition(57,7)
[Console]::Write("$j ")
Start-Sleep -Milliseconds 200
}
Start-Sleep -Milliseconds 200
}
[Console]::SetCursorPosition(40,5)
[Console]::Write(" `n")
[Console]::SetCursorPosition(40,7)
[Console]::Write(" `n")
[Console]::SetCursorPosition(0,0)
If the goal is strictly to overwrite powershell console prompt line (the current line with the cursor) then all the answers here work only to an extent, and in some ways doing more than is desired.
Raf's and Craig's answers that use the Clear-Host cmdlet (cls) in their first line, like Dullson noted, are doing too much. Blanking the entire screen assumes the things cleared are no longer important for viewing which may not be true. Sometimes these are necessary to make sense of the current line.
Raf's Write-Progress solution is a powerful cmdlet but seems like an overkill for just overwriting the current line.
Raf's Write-Host proposal, Matt's submission and Dullson's tweak are all good where only one character position at a definite screen position needs updating or where the succeeding line text is longer in length than the current. If not, the succeeding line text would only overwrite the current line to the extent of its length leaving those parts of the succeeded line whose length position is longer than the new to remain in view together with the new line.
For example, if the previous value was 10 and the new value is 9 what would be shown is 90. The 9 just overwrites the portion of the preceding value that is equal to its length - 1. So the solutions work well for increments but not so well for decrements where length of value reduces compared to previous.
The following block shows how to guarantee total (visual) overwrite of the current line text with a new one.
$LongString = "This string is long"
$ShortString = "This is short"
#Simulate typing a string on the console line
$L = 1
While ($L -le $LongString.Length)
{
$Sub = $LongString.Substring(0,$L)
Write-Host "`r$Sub" -NoNewline
$L++
# This sleep is just to simulate manual typing delay
Start-Sleep -Milliseconds 20
}
# Now blank out the entire line with the space character " "
# The quantity of spaces should be equal to the length of the current text
# Which in this case is contained in $Sub.Length
$Blank = " "
For($L = 1; $L -le $Sub.Length; $L++)
{
$Blank = $Blank + " "
}
Write-Host "`r$Blank" -NoNewline
# Overwrite the blank console line with the new string
$L = 1
While ($L -le $ShortString.Length)
{
$Sub = $ShortString.Substring(0,$L)
Write-Host "`r$Sub" -NoNewline
$L++
# This sleep is just to simulate delay in manual typing
Start-Sleep -Milliseconds 20
}
# The following is not required if you want the Powershell prompt
# to resume to the next line and not overwrite current console line.
# It is only required if you want the Powershell prompt to return
# to the current console line.
# You therefore blank out the entire line with spaces again.
# Otherwise prompt text might be written into just the left part of the last
# console line text instead of over its entirety.
For($L = 1; $L -le $Sub.Length; $L++)
{
$Blank = $Blank + " "
}
Write-Host "`r$Blank" -NoNewline
Write-Host "`r" -NoNewline
This one I got from a blog post by Thomas Rayner. He uses ANSI Escape Sequences to save the cursor position [s and update the cursor position [u
$E=[char]27
Then save the current cursor position using the save escape sequence:
"${E}[s"
Usage: Use the update sequence ${E}[u to tell PS where to start the string:
1..10 | %{"${E}[uThere are $_ s remaining"; Start-Sleep -Seconds 1}
Does not work in the ISE however.
I know links get stale but it is here today.
Try
for ($i=1;$i -le 100;$i++){Write-Host -NoNewline "`r" $i;sleep 1}
https://241931348f64b1d1.wordpress.com/2017/08/23/how-to-write-on-the-same-line-with-write-output/
This method worked for me to write output value in a loop until its status changed to "Succeeded". Ensure you set the cursor up by required number of lines and it overwrites the same line
while($val -ne 1)
{
if($taskstates.Tasks.state[0] -eq "Succeeded" -and $taskstates.Tasks.state[1] -eq "Succeeded" -and $taskstates.Tasks.state[2] -eq "Succeeded" -and $taskstates.Tasks.state[3] -eq "Succeeded")
{
$val = 1
}
#Clear-Host
$taskstates.Tasks.StartTime[0].ToString() +" "+ $taskstates.Tasks.name[0] +" is "+ $taskstates.Tasks.state[0]
$taskstates.Tasks.StartTime[1].ToString() +" "+ $taskstates.Tasks.name[1] +" is "+ $taskstates.Tasks.state[1]
$taskstates.Tasks.StartTime[2].ToString() +" "+ $taskstates.Tasks.name[2] +" is "+ $taskstates.Tasks.state[2]
$taskstates.Tasks.StartTime[3].ToString() +" "+ $taskstates.Tasks.name[3] +" is "+ $taskstates.Tasks.state[3]
$taskstates = Get-ASRJob -Name $failoverjob.Name
"ASR VMs build is in Progress"
Start-Sleep 5
[console]::setcursorposition($([console]::Cursorleft ),$([console]::CursorTop - 4))
}
I'm late to the party. Here's a proof of concept I recently discovered and adapted for my purposes. This example overwrites the line.
$count = 1
# Used for calculating the max number length for padding trailing spaces
$totalCount = 100
#Get current cursor position
$curCursorPos = New-Object System.Management.Automation.Host.Coordinates
$curCursorPos.X = $host.ui.rawui.CursorPosition.X
$curCursorPos.Y = $host.ui.rawui.CursorPosition.Y
# Counter code
While ($count -le 100) {
# Keep cursor in the same position on the same line
$host.ui.rawui.CursorPosition = $curCursorPos
# Display with padded trailing spaces to overwrite any extra digits
$pad = ($totalCount -as [string]).Length
# Display the counter
Write-Host "$(([string]$count).Padright($pad))" -NoNewline -ForegroundColor Green
# Run through the example quickly
Start-Sleep -Milliseconds 100
#increment $count
$count++
}
You can experiment with Write-Host -NoNewline property, by keeping it or removing it, to see which looks better for you.
I like below code...
$dots = ""
while (!$isTrue) {
if ($dots -eq "...") {
$dots = ""
}
else {
$dots += "."
}
Write-Host -NoNewLine "`rLoading$dots"
Start-Sleep 1
}
You can use $Host.UI.RawUI.WindowSize.Width to find the display width and then use .PadRight to fill up the line with spaces. This avoids having to clear the screen with each loop, the issue of characters persisted from the last loop, having to manipulate cursor position, or having to write a custom function or lots of cumbersome code, e.g.:
# only works in a console window
If ($Host.Name -eq "ConsoleHost")
{
Write-Host 'Starting...'
# find the max line length of the console host
$maxLineLength = $Host.UI.RawUI.WindowSize.Width
# loop a few times
For ($i = 1; $i -le 10; $i++)
{
# for the sake of demonstration, generate a random-length string of letters
$randStringLength = Get-Random -Minimum 1 -Maximum $maxLineLength
$randCharIndex = Get-Random -Minimum 65 -Maximum (65+26) # A = ASCII 65
$randChar = ([char]$randCharIndex)
$myString = [string]$randChar*$randStringLength
# overwrite at the current console line
Write-Host ("`r"+$myString.PadRight($maxLineLength," ")) -NoNewline
# pause briefly before going again
Start-Sleep -Milliseconds 200
}
Write-Host 'Done.'
}
Another option in PowerShell 7.2+ is to use the minimal Write-Progress view $PSStyle.Progress.View = Minimal:
# only works in a console window
If ($Host.Name -eq "ConsoleHost")
{
# loop a few times
For ($i = 1; $i -le 10; $i++)
{
# for the sake of demonstration, generate a random-length string of letters
$randStringLength = Get-Random -Minimum 1 -Maximum 500
$randCharIndex = Get-Random -Minimum 65 -Maximum (65+26) # A = ASCII 65
$randChar = ([char]$randCharIndex)
$myString = [string]$randChar*$randStringLength
# overwrite at the current console line
Write-Progress -Activity $i -Status $myString
# pause briefly before going again
Start-Sleep -Milliseconds 200
}
}
Alot of good suggestions here...
I use the WindowTitle bar for monitoring the status of my scripts, indicate where I am within my code, & the current progress.
For($t = 0; $t -le 100; $t++) {
$Host.UI.RawUI.WindowTitle = "Progress - $t% complete"
Start-Sleep -Milliseconds 10
}
I'll even insert updated "position" info within my code, to indicate where I'm at within my code:
$Host.UI.RawUI.WindowTitle = "Querying index..."
$Host.UI.RawUI.WindowTitle = "Updating search field..."
$Host.UI.RawUI.WindowTitle = "Conducting Robocopy..."
and of course when it's completed:
$Host.UI.RawUI.WindowTitle = "Script completed."
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 )