Home > database >  In Windows Powershell, how can I wait for an event to be true before proceeding?
In Windows Powershell, how can I wait for an event to be true before proceeding?

Time:03-04

I'm writing a Windows Powershell script that stops a service, then I want to print the service's status when it's finally stopped. I've tried the following, but it just hangs

$service = Get-Service | Where-Object {$_.name -like "*MyService*"}
Stop-Service -Name $service.name
$wait=true
while ($wait) {
  if($service.Status -eq "Running") {
    Start-Sleep -Seconds 1
  }
  else {
    $wait=$false
  }
}

I know I can probably write a for{} loop instead that counts 0-9, and breaks when my condition is met, but is there a better way?

CodePudding user response:

You need to re-check your service within the loop.

$service = Get-Service | Where-Object {$_.name -like "*MyService*"}
Stop-Service -Name $service.name
$wait=true
while ($wait) {
  if($service.Status -eq "Running") {
    Start-Sleep -Seconds 1
    # ADD THIS BELOW. Need to re-check service in loop.
    $service = Get-Service | Where-Object {$_.name -like "*MyService*"}
  }
  else {
    $wait=$false
  }
}

CodePudding user response:

tanstaafl's helpful answer addresses your immediate problem:

  • The .Status property value of a [System.ServiceProcess.ServiceController] instance (as returned by Get-Service) is a static value that only reflects the status of the service at the time of the Get-Service call.[1]
  • To update the value to reflect the then-current status, call the .Refresh() method.

However, there is no need to explicitly wait for a service to stop, because Stop-Service is synchronous, i.e.:

  • It waits for the service to finish stopping before returning, unless you explicitly pass -NoWait.
  • If that doesn't happen within a fixed, 2-second timeout:[2]
    • A warning is issued if the service last reported that stopping is pending - potentially, stopping will eventually finish.
    • Otherwise, a non-terminating error occurs - this suggests that the service is stuck.

Thus, you can simplify your code as follows:

# Report a script-terminating error if stopping doesn't finish
# within the timeout period.
Stop-Service -Name *MyService* -ErrorAction Stop -WarningAction Stop

More work is needed if you want to implement a retry mechanism.


[1] There is one exception, although the behavior is undocumented and should be considered an implementation detail: If you pipe a preexisting ServiceController instance to Stop-Service / Start-Service, these cmdlets refresh the instance for you; e.g., after executing ($service = Get-Service Bits) | Stop-Service, $service.Status is current (reflects Stopped).

[2] As of PowerShell Core 7.3.0-preview.2 - see the source code.

  • Related