Powershell 等待服务停止或启动

Powershell Wait for service to be stopped or started

我已经搜索过这个论坛和google,但找不到我需要的东西。 我有一个相当大的脚本,我正在寻找一些代码来检查服务是否启动或停止,然后再继续下一步。

它自己需要循环的函数,直到它停止或启动(将有一个用于停止的函数和一个用于启动的函数)。

一共有4个服务几乎同名,所以Service Bus *可以作为通配符。

以下将循环并验证给定服务的状态,直到具有 "Running" 状态的服务数量等于零(因此它们已停止),因此您可以在等待时使用它用于停止服务。

我已经添加了一个 $MaxRepeat 变量,这将永远阻止 运行ning。它将 运行 最大定义为 20 倍。

$services = "Service Bus *"
$maxRepeat = 20
$status = "Running" # change to Stopped if you want to wait for services to start

do 
{
    $count = (Get-Service $services | ? {$_.status -eq $status}).count
    $maxRepeat--
    sleep -Milliseconds 600
} until ($count -eq 0 -or $maxRepeat -eq 0)

我无法使用 Micky 发布的 'count' 策略,所以我是这样解决的:

我创建了一个函数,它接受一个 searchString(这可能是 "Service Bus *")和我期望服务应该达到的状态。

function WaitUntilServices($searchString, $status)
{
    # Get all services where DisplayName matches $searchString and loop through each of them.
    foreach($service in (Get-Service -DisplayName $searchString))
    {
        # Wait for the service to reach the $status or a maximum of 30 seconds
        $service.WaitForStatus($status, '00:00:30')
    }
}

现在可以使用

调用该函数
WaitUntilServices "Service Bus *" "Stopped"

WaitUntilServices "Service Bus *" "Running"

如果达到超时时间,将抛出一个不太优雅的异常:

Exception calling "WaitForStatus" with "2" argument(s): "Time out has expired and the operation has not been completed."

除了 this one liner might be useful if you just want to wait for a single service (also inspired by a post from Shay Levy):

(Get-Service SomeInterestingService).WaitForStatus('Running')

我不得不用多个计数器对此进行一些调整,因为该服务有意缓慢启动和停止。原始脚本让我走上了正确的轨道。我必须等待服务处于完全停止状态才能继续,因为我实际上是在重新启动相同的服务。 您或许可以删除 "sleep,",但我不介意将其保留。 您可能会删除所有内容并只使用 $stopped 变量。 :)

    # change to Stopped if you want to wait for services to start
    $running = "Running" 
    $stopPending = "StopPending"
    $stopped = "Stopped"
    do 
    {
        $count1 = (Get-Service $service | ? {$_.status -eq $running}).count
        sleep -Milliseconds 600
        $count2 = (Get-Service $service | ? {$_.status -eq $stopPending}).count
        sleep -Milliseconds 600
        $count3 = (Get-Service $service | ? {$_.status -eq $stopped}).count
        sleep -Milliseconds 600
    } until ($count1 -eq 0 -and $count2 -eq 0 -and $count3 -eq 1)

在我的 Azure build/deployment 管道中,我像这样使用它来启动和停止服务(在之前已经异步发送 'Stop' 命令之后)并且它适用于所有过渡状态,例如 StartingStoppingPausingResuming(在状态中称为 StartPendingStopPendingPausePendingContinuePending枚举 ServiceControllerStatus).

# Wait for services to be stopped or stop them
$ServicesToStop | ForEach-Object {
  $MyService = Get-Service -Name $_ -ComputerName $Server;
  while ($MyService.Status.ToString().EndsWith('Pending')) {
    Start-Sleep -Seconds 5;
    $MyService.Refresh();
  };
  $MyService | Stop-Service -WarningAction:SilentlyContinue;
  $MyService.Dispose();
};

这需要传统的 powershell 才能在远程服务器上运行,pwsh.exe 的 cmdlet 不包含参数 -ComputerName.

在我看来,不需要计数器,因为只有过渡状态会导致 cmdlet 失败,并且它们无论如何都会在不久的将来更改为受支持的状态之一(Stop 命令最多 125 秒)。

为我对@Christoph 的回复添加更多详细信息

这是我最近创建的一个脚本,用于停止服务并确保进程也停止。在我们的案例中,过程是可预测的。如果您有多个服务 运行 来自同一个可执行文件,则可能需要做更多的工作才能获得 service/processid 映射。

$MaxWait = 180 #seconds

$ServiceNames = "MyServiceName*"
$ProcName = 'MyServiceProcName' #for the services
    
$sw = [System.Diagnostics.Stopwatch]::StartNew() # to keep track of 
$WaitTS = (New-TimeSpan -Seconds $MaxServiceWait) #could also use a smaller interval if you want more progress updates
    
$InitialServiceState = get-service $ServiceNames | select Name,Status,StartType
    

    
write-Host "$ENV:COMPUTERNAME Stopping $ServiceNames"
    
$sw.Restart()
$Services = @()
$Services += Get-Service $ServiceNames | where Status -EQ Running | Stop-Service -PassThru -NoWait #nowait requires powershell 5+
$Services += Get-Service $ServiceNames | where Status -Like *Pending

#make sure the processes are actually stopped!
while (Get-Process | where Name -Match $ProcName)
{
    #if there were services still running
    if ($Services) {
        Write-Host "$ENV:COMPUTERNAME ...waiting up to $MaxServiceWait sec for $($Services.Name)"
        #wait for the service to stop
        $Services.WaitForStatus("Stopped",$WaitTS)
         
    }
    #if we've hit our maximum wait time
    if ($sw.Elapsed.TotalSeconds -gt $MaxServiceWait) {
        Write-Host "$ENV:COMPUTERNAME Waited long enough, killing processes!"
        Get-Process | where name -Match $ProcName | Stop-Process -Force
    }
    Start-Sleep -Seconds 1

    #get current service state and try and stop any that may still be running
    #its possible that another process tried to start a service while we were waiting
    $Services = @()
    $Services += Get-Service $ServiceNames | where Status -EQ Running | Stop-Service -PassThru  -NoWait #nowait requires powershell 5+
    $Services += Get-Service $ServiceNames | where Status -Like *Pending
}