简体   繁体   English

Powershell 检查服务是否启动的脚本,如果没有则启动它

[英]Powershell script to check if service is started, if not then start it

I have built a small powershell script to check if a service is started.我构建了一个小的 powershell 脚本来检查服务是否启动。 If it is not started, try to start it then wait one minute and check again.如果未启动,请尝试启动它,然后等待一分钟并再次检查。 Keep repeating this process until the service start successfully.不断重复这个过程,直到服务启动成功。 I have found the loop does not behave the way I expected in that I seem to have to re-assign the service variable within the loop in order to get the updated status.我发现循环的行为与我预期的不同,因为我似乎必须在循环内重新分配服务变量才能获得更新的状态。 Here is my code:这是我的代码:

$ServiceName = 'Serenade'
$arrService = Get-Service -Name $ServiceName

if ($arrService.Status -ne 'Running'){
$ServiceStarted = $false}
Else{$ServiceStarted = $true}

while ($ServiceStarted -ne $true){
Start-Service $ServiceName
write-host $arrService.status
write-host 'Service started'
Start-Sleep -seconds 60
$arrService = Get-Service -Name $ServiceName #Why is this line needed?
if ($arrService.Status -eq 'Running'){
$ServiceStarted = $true}
}

If I run the code without the third last line (the one with the comment), I get the following output. I can check in the Windows service manager and the service was clearly started after the first loop.如果我在没有倒数第三行(带注释的那一行)的情况下运行代码,我会得到以下 output。我可以检查 Windows 服务管理器,并且在第一个循环后显然启动了服务。 Why is that third last line required?为什么需要倒数第三行?

在此处输入图像描述

Given this behavior, is there a better way to write this code?鉴于这种行为,有没有更好的方法来编写这段代码?

Thank you谢谢

I think you may have over-complicated your code: If you are just checking to see if a service is running and, if not, run it and then stop re-evaluating, the following should suffice:我认为您的代码可能过于复杂:如果您只是检查服务是否正在运行,如果没有,则运行它然后停止重新评估,以下内容就足够了:

Good point on the refresh .好点就刷新

$ServiceName = 'Serenade'
$arrService = Get-Service -Name $ServiceName

while ($arrService.Status -ne 'Running')
{

    Start-Service $ServiceName
    write-host $arrService.status
    write-host 'Service starting'
    Start-Sleep -seconds 60
    $arrService.Refresh()
    if ($arrService.Status -eq 'Running')
    {
        Write-Host 'Service is now Running'
    }

}

Given $arrService = Get-Service -Name $ServiceName , $arrService.Status is a static property, corresponding to the value at the time of the call.给定$arrService = Get-Service -Name $ServiceName$arrService.Status是一个静态属性,对应于调用时的值。 Use $arrService.Refresh() when needed to renew the properties to current values.需要时使用$arrService.Refresh()将属性更新为当前值。

MSDN ~ ServiceController.Refresh() MSDN ~ ServiceController.Refresh()

Refreshes property values by resetting the properties to their current values.通过将属性重置为其当前值来刷新属性值。

一个可能更简单的解决方案:

get-service "servicename*" | Where {$_.Status -neq 'Running'} | start-service

Combining Alaa Akoum and Nick Eagle's solutions allowed me to loop through a series of windows services and stop them if they're running.结合 Alaa Akoum 和 Nick Eagle 的解决方案,我可以循环访问一系列 Windows 服务,并在它们正在运行时停止它们。

# stop the following Windows services in the specified order:
[Array] $Services = 'Service1','Service2','Service3','Service4','Service5';

# loop through each service, if its running, stop it
foreach($ServiceName in $Services)
{
    $arrService = Get-Service -Name $ServiceName
    write-host $ServiceName
    while ($arrService.Status -eq 'Running')
    {
        Stop-Service $ServiceName
        write-host $arrService.status
        write-host 'Service stopping'
        Start-Sleep -seconds 60
        $arrService.Refresh()
        if ($arrService.Status -eq 'Stopped')
            {
              Write-Host 'Service is now Stopped'
            }
     }
 }

The same can be done to start a series of service if they are not running:如果它们没有运行,可以执行相同的操作来启动一系列服务:

# start the following Windows services in the specified order:
[Array] $Services = 'Service1','Service2','Service3','Service4','Service5';

# loop through each service, if its not running, start it
foreach($ServiceName in $Services)
{
    $arrService = Get-Service -Name $ServiceName
    write-host $ServiceName
    while ($arrService.Status -ne 'Running')
    {
        Start-Service $ServiceName
        write-host $arrService.status
        write-host 'Service starting'
        Start-Sleep -seconds 60
        $arrService.Refresh()
        if ($arrService.Status -eq 'Running')
        {
          Write-Host 'Service is now Running'
        }
    }
}
$ServiceName = 'Serenade'
$arrService = Get-Service -Name $ServiceName

while ($arrService.Status -ne 'Running')
{

    Start-Service $ServiceName
    write-host $arrService.status
    write-host 'Service starting'
    Start-Sleep -seconds 60
    $arrService.Refresh()
    if ($arrService.Status -eq 'Running')
    {
        Write-Host 'Service is now Running'
    }

}

Can we use the same script for multiple services and send a single email if services are down.我们是否可以对多个服务使用相同的脚本并在服务关闭时发送一封电子邮件。

[Array] $servers = "Server1","server2";
$service='YOUR SERVICE'

foreach($server in $servers)

{
    $srvc = Get-WmiObject -query "SELECT * FROM win32_service  WHERE   name LIKE '$service' " -computername $server  ;
    $res=Write-Output $srvc | Format-Table -AutoSize $server, $fmtMode, $fmtState, $fmtStatus ;  
   $srvc.startservice() 
   $res
}

The below is a compact script that will check if "running" and attempt start service until the service returns as running.下面是一个紧凑的脚本,它将检查是否“正在运行”并尝试启动服务,直到服务返回运行状态。

$Service = 'ServiceName'
If ((Get-Service $Service).Status -ne 'Running') {
   do {
       Start-Service $Service -ErrorAction SilentlyContinue
       Start-Sleep 10
   } until ((Get-Service $Service).Status -eq 'Running')
} Return "$($Service) has STARTED"

Trying to do things as smooth as possible - I here suggest modifying GuyWhoLikesPowershell's suggestion slightly.尽量让事情顺利进行 - 我在这里建议稍微修改 GuyWhoLikesPowershell 的建议。

I replaced the if and until with one while - and I check for "Stopped", since I don't want to start if status is "starting" or " Stopping".我用一段时间替换了 if 和 until - 我检查了“Stopped”,因为如果状态是“starting”或“Stopping”,我不想开始。

$Service = 'ServiceName'
while ((Get-Service $Service).Status -eq 'Stopped') 
{
    Start-Service $Service -ErrorAction SilentlyContinue
    Start-Sleep 10
} 
Return "$($Service) has STARTED"

In the spirit of Enterprise FizzBuzz , I wrote a version with extensive logging and handling for errors and Pending service states.本着Enterprise FizzBu​​zz的精神,我编写了一个包含大量日志记录和处理错误和待处理服务状态的版本。 It's likely over-engineered for most use cases.对于大多数用例来说,它可能被过度设计了。 Please note this version is specifically intended be invoked by a Windows Scheduled Task on an interval.请注意,此版本专门由 Windows 计划任务按时间间隔调用。 It runs for a specified number of tries on each run before stopping.在停止之前,它会在每次运行时运行指定次数的尝试。

<#
.SYNOPSIS
This script attempts to Start the specified Windows service if the service is stopped and logs its attempts to a file.
It's intended to be deployed as a Windows Scheduled Task that runs every few minutes. 
Please ensure the Scheduled Task interval is longer than (($secondsBeforeRetry + 10s) * $maxTries) to avoid concurrent running script instances.

The script first checks the existence of the log file and if not found creates a new one. 
It then checks the status of the specified Windows service. 
If it's not Stopped (implicitly Running), no actions occur and the script completes without logging anything.
If it's Stopped, the script attempts to Start it and logs success or failure. 
If the service is still Stopped, the script will try to Start it again up to $maxTries times.
If the service is in a Starting (StartPending) or Stopping (StopPending) state, the script will recheck its status for 
up to three $secondsBeforeRetry intervals to see if it changes. 
If the service is still Pending after three checks, the process is likely hung and the script will attempt to force taskkill it.

References: 
List of ServiceControllerStatus states: https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontrollerstatus?view=dotnet-plat-ext-6.0
The script does not handle the Paused, PausedPending, or ContinuePending service states.
#>


param (
    # Specifies the service name
    [Parameter(Mandatory = $true)]
    [string]$serviceName,
    
    # Specifies the directory for the log file
    [Parameter(Mandatory = $false)]
    [string]$logPath = 'C:\Scripts\Logs',
    
    # Specifies the max number of service Start attempts per run
    [Parameter(Mandatory = $false)]
    [int]$maxTries = 3,
    
    # Specifies the number of seconds to wait before trying again after a failed start attempt or before rechecking a Pending status
    [Parameter(Mandatory = $false)]
    [int]$secondsBeforeRetry = 30

)

$service = Get-Service $serviceName
$logName = $serviceName + 'AutoRestartLog.txt'
$logFullPath = Join-Path -Path $logPath -ChildPath $logName

if(!(Test-Path -Path $logFullPath)) {
    $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
    New-Item -Path $logPath -Name $logName -ItemType "file" -Value "## Log file for $serviceName auto-start script ##`n## Created [$timestamp] ##`n" -Force
    Start-Sleep -s 1
}

if ($service.Status -eq 'Stopped') {
    $tries = 0
    
    while (($tries -lt $maxTries) -and ($service.Status -ne 'Running')) {
        if ($tries -eq 0) {
            $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
            $logSvcStoppedMsg = "[$timestamp] $serviceName service status is $($service.Status), attempting to Start. Will try up to $maxTries times."
            Add-Content -Path $logFullPath -Value $logSvcStoppedMsg
        }
        
        try {
            Start-Service $ServiceName -ErrorAction Stop # Throws exception if command fails, for example if the service is Disabled
            $tries += 1
            Start-Sleep -s 10
            $service.Refresh()
            if ($service.Status -eq 'Running') {
                $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                $logSvcStartSuccessMsg = "[$timestamp] Attempt #$tries - $serviceName service status is now $($service.Status)"
                Add-Content -Path $logFullPath -Value $logSvcStartSuccessMsg
            }

        }
        catch {
            $errorMsg = $_
            $tries += 1
            Start-Sleep -s 10
            $service.Refresh()
            $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
            $logSvcStartExceptionMsg = "[$timestamp] Attempt #$tries - $serviceName service status is $($service.Status) and failed to start with error:`n    $errorMsg"
            Add-Content -Path $logFullPath -Value $logSvcStartExceptionMsg
            if ($tries -ne $maxTries) {
                Start-Sleep -s $secondsBeforeRetry
            }
        }
        
        $service.Refresh()
        if (($service.Status -eq 'StartPending') -or ($service.Status -eq 'StopPending')) {
            $maxStateChecks = 3
            $stateChecks = 0
            
            while (($stateChecks -lt $maxStateChecks) -and (($service.Status -eq 'StartPending') -or ($service.Status -eq 'StopPending'))) {
                $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                $logSvcStillPendingMsg = "[$timestamp] Attempt #$tries - $serviceName service status is $($service.Status). This may indicate the service is hung in the Starting or Stopping state. Waiting another $secondsBeforeRetry seconds before checking again."
                Add-Content -Path $logFullPath -Value $logSvcStillPendingMsg
                $stateChecks += 1
                if ($stateChecks -ne $maxStateChecks) {
                    Start-Sleep -s $secondsBeforeRetry
                }
                $service.Refresh()
                if ($service.Status -eq 'Running') {
                    $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                    $logSvcStartSuccessMsg = "[$timestamp] Attempt #$tries - $serviceName service status is now $($service.Status)"
                    Add-Content -Path $logFullPath -Value $logSvcStartSuccessMsg
                } elseif ($service.Status -eq 'Stopped') {
                    $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                    $logSvcStoppedAfterPendingMsg = "[$timestamp] Attempt #$tries - $serviceName service status is now $($service.Status). If any Start attempts remain the script will try again on the next loop."
                    Add-Content -Path $logFullPath -Value $logSvcStoppedAfterPendingMsg
                }
            }
            
            $service.Refresh()
            if (($stateChecks -eq $maxStateChecks) -and (($service.Status -eq 'StartPending') -or ($service.Status -eq 'StopPending'))) {
                $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                $logSvcStuckPendingMsg = "[$timestamp] Attempt #$tries - $serviceName service status appears stuck in a Pending (Starting or Stopping) state. Attempting to force kill process."
                Add-Content -Path $logFullPath -Value $logSvcStuckPendingMsg
                
                $servicePID = Get-CimInstance -Class Win32_Service -Filter "Name LIKE '$serviceName'" | Select-Object -ExpandProperty ProcessId
                $resultMsg = taskkill /pid $servicePID /f
                $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                $logSvcTaskKillMsg = "[$timestamp] Attempt #$tries - taskkill result:`n    $resultMsg"
                Add-Content -Path $logFullPath -Value $logSvcTaskKillMsg
                
                Start-Sleep -s 10
                $service.Refresh()
                $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
                $logSvcStatusMsg = "[$timestamp] Attempt #$tries - $serviceName service status is now $($service.Status)"
                Add-Content -Path $logFullPath -Value $logSvcStatusMsg
            }
        }
        
        if (($tries -eq $maxTries) -and ($service.Status -ne 'Running')) {
            $timestamp = (Get-Date -Format yyyy-MM-dd--HH-mm-ss)
            $logNoMoreAttemptsMsg = "[$timestamp] $serviceName service status is $($service.Status) after $maxTries Start attempts. This run of the script will not continue trying to Start the service."
            Add-Content -Path $logFullPath -Value $logNoMoreAttemptsMsg
        }
    }
    
    # Add line break at end of log file entry
    Add-Content -Path $logFullPath -Value "`n"
}

It outputs a log file that looks like this:它输出一个如下所示的日志文件:

## Log file for Netman auto-start script ##
## Created [2022-07-11--22-55-38] ##

[2022-07-11--23-25-53] Netman service status is Stopped, attempting to Start. Will try up to 3 times.
[2022-07-11--23-26-03] Attempt #1 - Netman service status is Stopped and failed to start with error:
    Service 'Network Connections (Netman)' cannot be started due to the following error: Cannot start service Netman on computer '.'.
[2022-07-11--23-26-43] Attempt #2 - Netman service status is Stopped and failed to start with error:
    Service 'Network Connections (Netman)' cannot be started due to the following error: Cannot start service Netman on computer '.'.
[2022-07-11--23-27-23] Attempt #3 - Netman service status is Stopped and failed to start with error:
    Service 'Network Connections (Netman)' cannot be started due to the following error: Cannot start service Netman on computer '.'.
[2022-07-11--23-27-53] Netman service status is Stopped after 3 Start attempts. This run of the script will not continue trying to Start the service.

[2022-07-12--01-46-15] Netman service status is Stopped, attempting to Start. Will try up to 3 times.
[2022-07-12--01-46-25] Attempt #1 - Netman service status is now Running

Start the service if it is stopped如果服务停止,则启动该服务

Get-Service "servicename*" | Where {$_.Status -ne 'Stopped'} | Start-Service

Context语境

We have a product that has many variations and we work on them at once.我们的产品有很多变体,我们会同时处理它们。 Each variations has its own service.每个变体都有自己的服务。 When multiple people work on the same version and one person manages to crash the service then someone needs to connect on the machine remotely and fix it.当多个人在同一版本上工作并且一个人设法使服务崩溃时,则需要有人远程连接到机器上并修复它。

Solution解决方案

So I made something that could manage multiple instances.所以我做了一些可以管理多个实例的东西。 Luckily all of them have the same prefix and Get-Service can take a "wildcard" .幸运的是,它们都具有相同的前缀,并且Get-Service可以使用“通配符”

$ServiceName = 'ASService_*'; # Your wildcard goes here.

while ($true)
{
    Get-Service $ServiceName | Where {$_.Status -eq 'Stopped'} | Foreach-Object {
        Start-Service $_;
        # Logging it so that you know it was you.
        Get-Date;
        Write-Output "``$($_.Name)`` has stopped running.";
    }
    Start-Sleep -Seconds 1;
}

This can be further simplified if you don't care about logs.如果您不关心日志,这可以进一步简化。

$ServiceName = 'ASService_*'; # Your wildcard goes here.

while ($true)
{
    Get-Service $ServiceName | Where {$_.Status -eq 'Stopped'} | Foreach-Object { Start-Service $_ };
    Start-Sleep -Seconds 1;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM