如何通过批处理文件检查服务是否在运行并启动它,如果它没有运行?

115

我想编写一个批处理文件,执行以下操作:

  • 检查服务是否正在运行
    • 如果正在运行,则退出批处理
    • 如果没有运行,则启动服务

到目前为止,我搜索到的代码示例都不能正常工作,所以我决定不将它们发布。

启动服务可通过以下方式完成:

net start "SERVICENAME"
  1. 如何检查服务是否在运行,以及如何在批处理文件中进行if语句的编写?
  2. 我有点困惑。我需要传递到 net start 命令的参数是服务名称还是显示名称?

3
脏编程:当你想要的是如果服务没有运行就发出启动命令来启动服务。如果服务没有运行,则它将启动服务。如果它正在运行,则会收到错误消息,但服务仍在运行(且不停止)。肮脏但有效。然而,如果您只想在必须启动服务时执行其他命令,则一定要使用lc的更干净的版本。 - Peter Schuetze
@Peter Schuetze:是的,如果启动服务是唯一目的,那么您的反对意见是正确的。我还包括了记录开始等操作,所以我坚持使用了 lc 的解决方案。 - citronas
14个回答

195

要检查服务的状态,请使用sc query <SERVICE_NAME>。对于批处理文件中的if块,请查看文档

以下代码将检查服务MyServiceName的状态,并在其未运行时启动它(如果服务未运行,则执行if块):

for /F "tokens=3 delims=: " %%H in ('sc query "MyServiceName" ^| findstr "        STATE"') do (
  if /I "%%H" NEQ "RUNNING" (
   REM Put your code you want to execute here
   REM For example, the following line
   net start "MyServiceName"
  )
)

它的作用解释如下:

  1. 查询服务的属性。
  2. 查找包含文本“STATE”的行。
  3. 对该行进行标记化,并提取第3个标记,即包含服务状态的标记。
  4. 将结果状态与字符串“RUNNING”进行比较。

至于您的第二个问题,您需要传递给 net start 的参数是服务名称,而不是显示名称。


太棒了。感谢你的努力。不幸的是它没有起作用?也许我对此太蠢了。我做了一个测试,将“MyServiceName”替换为“SCardSvr”(转义),把所有内容放入批处理文件中并执行,但服务没有启动。即使我用其他东西代替net start,例如打印到文件中,也无法执行。您能再看一眼吗?=) - citronas
抱歉,我在第一行多写了一些东西...请尝试这个。如果不起作用,当您从命令行运行 sc query "SCardSvr" 时会发生什么? - lc.
1
你在引号中有“SCardSvr”吗?我不认为应该这样。 - LittleBobbyTables - Au Revoir
你应该意识到可以简化第一行代码,而且得到相同的结果。使用:for /F "tokens=3 delims=: " %%H in ('sc query "MyServiceName"^|find /i "STATE"') do ( - Leptonator
1
对于任何路过的人,有一个小提示。在 Powershell 中,您需要添加“.exe”:sc.exe query "MyServiceName"。因为 scSet-Content 的别名(您可以使用 Get-Alias "sc" 进行检查)。 - Henk Poley
显示剩余2条评论

37
要切换服务,请使用以下命令:

NET START "Distributed Transaction Coordinator"(启动分布式事务协调器) || NET STOP "Distributed Transaction Coordinator"(停止分布式事务协调器)


1
它能够工作是因为从开始的退出代码。如果启动命令失败,那很可能是因为它已经在运行了(无论如何随后停止它都不会有影响),所以你可以尝试停止它。 - lc.
3
该命令的结构是这样的:如果net start失败,则会停止它(由于“||”表示否则),但如果net start运行,则net stop不会执行。太棒了! - Doctor DOS
1
这是我认为最好的答案,也许@citronas应该考虑将其标记为被接受的答案:简单、聪明、优雅,非常适合像StackOverflow这样的网站。让它变得简单! - Sopalajo de Arrierez
2
不想太挑剔(好吧,也许只是有点),但 || 实际上是一个 OR 运算符,尽管在这种情况下它在功能上是一个 ELSE 语句。这是一个微妙但重要的区别。我仍然给你点赞——我经常在 Unix/Linux shell 脚本中使用它,不知道为什么我从来没有想过在 Windows 批处理中这样做。 - Deacon
这似乎过于简化了,我永远不想将其用于批处理自动化或交给他人使用...但对于我需要快速切换的服务,在我的桌面上放一个快捷图标,这正是所需。 - Joel Coehoorn

23
您可以使用以下命令来查看服务是否正在运行:
sc query [ServiceName] | findstr /i "STATE"

当我在我的NOD32防病毒软件上运行它时,会得到:

STATE                       : 4 RUNNING
如果它已停止,我会得到:
STATE                       : 1 STOPPED

您可以将此用作变量,然后确定是否使用NET START。

服务名称应该是服务名称,而不是显示名称。


3
感谢您的帮助,在尝试整合您发布的内容时,我大大提高了我的批处理技能 ;) - citronas

16

应该可以了:

FOR %%a IN (%Svcs%) DO (SC query %%a | FIND /i "RUNNING"
IF ERRORLEVEL 1 SC start %%a)

16

不受语言限制的版本。

@Echo Off
Set ServiceName=Jenkins


SC queryex "%ServiceName%"|Find "STATE"|Find /v "RUNNING">Nul&&(
    echo %ServiceName% not running 
    echo Start %ServiceName%

    Net start "%ServiceName%">nul||(
        Echo "%ServiceName%" wont start 
        exit /b 1
    )
    echo "%ServiceName%" started
    exit /b 0
)||(
    echo "%ServiceName%" working
    exit /b 0
)

是的!“空格键”总是突然袭击你的背部! - Dr.eel
2
几乎完美的答案。我只会修复这一行: Net start“%ServiceName%”> nul ||( - Cesar
我已经将脚本制作成通用的,这样就可以在Windows计划任务中使用了。只需将“Set ServiceName=Jenkins”替换为“Set ServiceName=%~1”,并像这样调用它:“watch-service.bat“Jenkins””。 - Dr.eel
@Ant_222 它使用了“queryex”而不是“query”,这与语言有关。你为什么认为它不是 Windows 批处理?你尝试过使用它吗? - Dr.eel
1
/v 用于显示不匹配的字符串。查找包含 STATE 的字符串,但不包含 RUNNING。 - Dr.eel
显示剩余2条评论

5

我刚刚发现这个帖子,想要增加讨论的内容,如果这个人不想使用批处理文件来重新启动服务。在Windows中,如果您转到服务、服务属性,然后是恢复选项卡。在这里,您可以设置服务的参数。例如,在服务停止时重新启动服务。此外,您甚至可以有第二次失败尝试做一些不同的事情,比如重新启动计算机。


2
那是一个有用的答案,但需要注意的是,只有当服务使用exit(-1)停止时才能起作用。如果服务优雅地退出(即使带有错误消息),自动恢复也不会触发。如果服务被用户手动杀死,它也不会触发。 - Art Gertner
@sagelightning:我们需要管理员权限来尝试这种方法。 - Kumar M
@ArtGertner - 通过任务管理器终止服务进程将被解释为“崩溃”,并触发恢复。 - Martin Ba

4

当使用西班牙语的Windows时,代码应该如下:

for /F "tokens=3 delims=: " %%H in ('sc query MYSERVICE ^| findstr "        ESTADO"') do (
  if /I "%%H" NEQ "RUNNING" (
    REM Put your code you want to execute here
    REM For example, the following line
    net start MYSERVICE
  )
)

将 MYSERVICE 替换为要处理的服务名称。您可以通过查看服务属性来查看服务名称。


11
如果你用英语回答会更好,因为这对每个人都有好处。 - j0k
2
不确定为什么会被踩。这是一个重要的观点,也是尽可能避免字符串比较的原因。在这种情况下,您必须根据目标 Windows 安装的默认语言更改字符串。 - lc.
2
@lc:是否包含每种语言的答案是合理的?引用(或包含)一个资源,它说出了在给定语言中搜索哪个字符串可能更有用。 - Mike Bailey

4

对于Windows Server 2012,以下是我使用的方法。仅用实际服务名称替换“SERVICENAME”:

@ECHO OFF
SET SvcName=SERVICENAME

SC QUERYEX "%SvcName%" | FIND "STATE" | FIND /v "RUNNING" > NUL && (
    ECHO %SvcName% is not running 
    ECHO START %SvcName%

    NET START "%SvcName%" > NUL || (
        ECHO "%SvcName%" wont start 
        EXIT /B 1
    )
    ECHO "%SvcName%" is started
    EXIT /B 0
) || (
    ECHO "%SvcName%" is running
    EXIT /B 0
)

无法工作。即使服务不存在,它仍然会显示服务正在运行。 - Malcolm Who

2
@echo off

color 1F


@sc query >%COMPUTERNAME%_START.TXT


find /I "AcPrfMgrSvc" %COMPUTERNAME%_START.TXT >nul

IF ERRORLEVEL 0 EXIT

IF ERRORLEVEL 1 NET START "AcPrfMgrSvc"

1
使用Powershell脚本启动服务。您可以将其链接到任务计划程序并按间隔或需要触发它。 将此创建为PS1文件,即扩展名为PS1的文件,然后让该文件从任务计划程序触发。
要启动停止服务
在任务计划程序中,如果您在服务器上使用它,请在参数中使用以下内容:
-noprofile -executionpolicy bypass -file "C:\Service Restart Scripts\StopService.PS1"
通过在cmd上运行相同的验证,如果它可以工作,则应该也可以在任务计划程序上工作。
$Password = "Enter_Your_Password"
$UserAccount = "Enter_Your_AccountInfor"
$MachineName = "Enter_Your_Machine_Name"
$ServiceList = @("test.SocketService","test.WcfServices","testDesktopService","testService")
$PasswordSecure = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $UserAccount, $PasswordSecure 

$LogStartTime = Get-Date -Format "MM-dd-yyyy hh:mm:ss tt"
$FileDateTimeStamp = Get-Date -Format "MM-dd-yyyy_hh"
$LogFileName = "C:\Users\krakhil\Desktop\Powershell\Logs\StartService_$FileDateTimeStamp.txt" 


#code to start the service

"`n####################################################################" > $LogFileName
"####################################################################" >> $LogFileName
"######################  STARTING SERVICE  ##########################" >> $LogFileName

for($i=0;$i -le 3; $i++)
{
"`n`n" >> $LogFileName
$ServiceName = $ServiceList[$i]
"$LogStartTime => Service Name: $ServiceName" >> $LogFileName

Write-Output "`n####################################"
Write-Output "Starting Service - " $ServiceList[$i]

"$LogStartTime => Starting Service: $ServiceName" >> $LogFileName
Start-Service $ServiceList[$i]

$ServiceState = Get-Service | Where-Object {$_.Name -eq $ServiceList[$i]}

if($ServiceState.Status -eq "Running")
{
"$LogStartTime => Started Service Successfully: $ServiceName" >> $LogFileName
Write-Host "`n Service " $ServiceList[$i] " Started Successfully"
}
else
{
"$LogStartTime => Unable to Stop Service: $ServiceName" >> $LogFileName
Write-Output "`n Service didn't Start. Current State is - "
Write-Host $ServiceState.Status
}
}

#code to stop the service

"`n####################################################################" > $LogFileName
"####################################################################" >> $LogFileName
"######################  STOPPING SERVICE  ##########################" >> $LogFileName

for($i=0;$i -le 3; $i++)
{
"`n`n" >> $LogFileName
$ServiceName = $ServiceList[$i]
"$LogStartTime => Service Name: $ServiceName" >> $LogFileName

Write-Output "`n####################################"
Write-Output "Stopping Service - " $ServiceList[$i]

"$LogStartTime => Stopping Service: $ServiceName" >> $LogFileName
Stop-Service $ServiceList[$i]

$ServiceState = Get-Service | Where-Object {$_.Name -eq $ServiceList[$i]}

if($ServiceState.Status -eq "Stopped")
{
"$LogStartTime => Stopped Service Successfully: $ServiceName" >> $LogFileName
Write-Host "`n Service " $ServiceList[$i] " Stopped Successfully"
}
else
{
"$LogStartTime => Unable to Stop Service: $ServiceName" >> $LogFileName
Write-Output "`nService didn't Stop. Current State is - "
Write-Host $ServiceState.Status
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接