远程启动/停止Windows服务并等待其打开/关闭

70

这个问题的最佳答案告诉我如何停止/启动远程服务。很好。

现在,我需要的是等待实际的停止/启动完成。因此,我要找的是一条命令行命令:

  1. 启动服务,在服务启动完成后返回(或超时并提高错误级别)
  2. 停止服务,在服务停止完成后返回

我自己也很想知道这个。我不相信使用SC命令是可能的,因为它本质上是异步的。你可能需要将该命令与服务状态的ping绑定在一起,以便在从STOPPING到STOPPED和从STARTING到STARTED时进行更改。 - David Andres
11个回答

124
我创建了一组批处理脚本,使用sc.exe实现这个功能。它们附在下面。要运行这些脚本,您应该是目标计算机上具有管理权限的用户,并从属于同一个域的计算机上运行。它可以设置为能够从域外运行(例如从VPN),但必须解决涉及防火墙、DCOM和安全凭据的多层安全性工作。有一天,我会找到PowerShell等效命令,那应该更容易一些。 < h2 > safeServiceStart.bat
@echo off
:: This script originally authored by Eric Falsken

IF [%1]==[] GOTO usage
IF [%2]==[] GOTO usage

ping -n 1 %1 | FIND "TTL=" >NUL
IF errorlevel 1 GOTO SystemOffline
SC \\%1 query %2 | FIND "STATE" >NUL
IF errorlevel 1 GOTO SystemOffline

:ResolveInitialState
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartedService
SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc \\%1 query %2 | Find "STATE"
timeout /t 2 /nobreak >NUL
GOTO ResolveInitialState

:StartService
echo Starting %2 on \\%1
sc \\%1 start %2 >NUL

GOTO StartingService
:StartingServiceDelay
echo Waiting for %2 to start
timeout /t 2 /nobreak >NUL
:StartingService
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 1 GOTO StartingServiceDelay

:StartedService
echo %2 on \\%1 is started
GOTO:eof

:SystemOffline
echo Server \\%1 is not accessible or is offline
GOTO:eof

:usage
echo %0 [system name] [service name]
echo Example: %0 server1 MyService
echo.
GOTO:eof

safeServiceStop.bat

@echo off
:: This script originally authored by Eric Falsken

IF [%1]==[] GOTO usage
IF [%2]==[] GOTO usage

ping -n 1 %1 | FIND "TTL=" >NUL
IF errorlevel 1 GOTO SystemOffline
SC \\%1 query %2 | FIND "STATE" >NUL
IF errorlevel 1 GOTO SystemOffline

:ResolveInitialState
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopedService
SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc \\%1 query %2 | Find "STATE"
timeout /t 2 /nobreak >NUL
GOTO ResolveInitialState

:StopService
echo Stopping %2 on \\%1
sc \\%1 stop %2 %3 >NUL

GOTO StopingService
:StopingServiceDelay
echo Waiting for %2 to stop
timeout /t 2 /nobreak >NUL
:StopingService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 1 GOTO StopingServiceDelay

:StopedService
echo %2 on \\%1 is stopped
GOTO:eof

:SystemOffline
echo Server \\%1 or service %2 is not accessible or is offline
GOTO:eof

:usage
echo Will cause a remote service to STOP (if not already stopped).
echo This script will waiting for the service to enter the stopped state if necessary.
echo.
echo %0 [system name] [service name] {reason}
echo Example: %0 server1 MyService
echo.
echo For reason codes, run "sc stop"
GOTO:eof

safeServiceRestart.bat

@echo off
:: This script originally authored by Eric Falsken

if [%1]==[] GOTO usage
if [%2]==[] GOTO usage

ping -n 1 %1 | FIND "TTL=" >NUL
IF errorlevel 1 GOTO SystemOffline
SC \\%1 query %2 | FIND "STATE" >NUL
IF errorlevel 1 GOTO SystemOffline

:ResolveInitialState
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
echo Service State is changing, waiting for service to resolve its state before making changes
sc \\%1 query %2 | Find "STATE"
timeout /t 2 /nobreak >NUL
GOTO ResolveInitialState

:StopService
echo Stopping %2 on \\%1
sc \\%1 stop %2 %3 >NUL

GOTO StopingService
:StopingServiceDelay
echo Waiting for %2 to stop
timeout /t 2 /nobreak >NUL
:StopingService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
IF errorlevel 1 GOTO StopingServiceDelay

:StopedService
echo %2 on \\%1 is stopped
GOTO StartService

:StartService
echo Starting %2 on \\%1
sc \\%1 start %2 >NUL

GOTO StartingService
:StartingServiceDelay
echo Waiting for %2 to start
timeout /t 2 /nobreak >NUL
:StartingService
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
IF errorlevel 1 GOTO StartingServiceDelay

:StartedService
echo %2 on \\%1 is started
GOTO:eof

:SystemOffline
echo Server \\%1 or service %2 is not accessible or is offline
GOTO:eof

:usage
echo Will restart a remote service, waiting for the service to stop/start (if necessary)
echo.
echo %0 [system name] [service name] {reason}
echo Example: %0 server1 MyService
echo.
echo For reason codes, run "sc stop"
GOTO:eof

3
请注意,使用ping命令检查服务器是否可用时,默认假定该服务器使用IPv4。最好加上“-4”选项来强制ping使用IPv4。 - Benjamin Wegman
1
我希望我有更多的赞可以给。这太棒了。 - Anton
2
@EricFalsken - 在你(非常棒的)脚本中,“系统离线”检测位在处理ipv6地址时存在问题,因为ping不会为ipv6地址打印TTL。添加-4可以强制使用ipv4 ping,但这并不总是可行的。 - radai
1
很棒的脚本!我发现我确实需要使用ping -4,并按下面建议的替换timeout为ping。此外,我认为最好删除sc start/stop命令中的>NULL,这样您可以看到任何异常消息。我还添加了“IF errorlevel 1 EXIT 1”,以便在出现错误(如访问被拒绝)时停止脚本,而不是永远等待。 - Henno Vermeulen
谢谢你提供这个脚本。在我们的环境中,我发现sc query命令执行时间很长。你有什么建议吗? - Gambit
显示剩余8条评论

10

那 Powershell 和 WaitForStatus 怎么样?例如,下面的脚本将重新启动远程机器上的 SQL Server:

$computer = "COMPUTER_NAME"
$me = new-object -typename System.Management.Automation.PSCredential -argumentlist "DOMAIN\user", (convertto-securestring "password" -asplaintext -force)
$restartSqlServer = { 
    $sqlServer = get-service mssqlserver
    $waitInterval = new-timespan -seconds 5
    if (-not ($sqlServer.Status -eq "Stopped")) {
        $sqlServer.Stop()
        $sqlServer.WaitForStatus('Stopped', $waitInterval) 
    }
    $sqlServer.Start()
    $sqlServer.WaitForStatus('Running', $waitInterval) 
}     
icm -ComputerName $computer -ScriptBlock $restartSqlServer -Credential $me 

你怎么称呼它?我对PowerShell的记忆已经忘得比记得的还多。 - JustEngland
1
@JustEngland - 只需将代码放入一个以“.ps1”扩展名命名的文件中,并从Poweshell控制台或ISE中运行它即可完成工作。 - andreister

8

我从未见过专门做这件事情的工具,但是使用C\C#\VB或者其他可以轻松访问服务API的语言编写这样的实用程序非常容易。以下是C#示例:

using System;
using System.ComponentModel;
using System.ServiceProcess;

namespace SCSync
{
    class Program
    {
        private const int ERROR_SUCCESS = 0;

        private const int ERROR_INVALID_COMMAND_LINE = 1;
        private const int ERROR_NO_ACCESS = 2;
        private const int ERROR_COMMAND_TIMEOUT = 3;
        private const int ERROR_NO_SERVICE = 4;
        private const int ERROR_NO_SERVER = 5;
        private const int ERROR_INVALID_STATE = 6;
        private const int ERROR_UNSPECIFIED = 7;

        static int Main(string[] args)
        {

            if (args.Length < 2 || args.Length > 4)
            {
                ShowUsage();
                return ERROR_INVALID_COMMAND_LINE;
            }

            string serviceName = args[0];
            string command = args[1].ToUpper();
            string serverName = ".";
            string timeoutString = "30";
            int timeout;

            if (args.Length > 2)
            {
                if (args[2].StartsWith(@"\\"))
                {
                    serverName = args[2].Substring(2);
                    if (args.Length > 3)
                    {
                        timeoutString = args[3];
                    }
                }
                else
                {
                    timeoutString = args[2];
                }
            }

            if (!int.TryParse(timeoutString, out timeout))
            {
                Console.WriteLine("Invalid timeout value.\n");
                ShowUsage();
                return ERROR_INVALID_COMMAND_LINE;
            }

            try
            {
                ServiceController sc = new ServiceController(serviceName, serverName);
                switch (command)
                {
                    case "START":
                        sc.Start();
                        sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeout));
                        break;
                    case "STOP":
                        sc.Stop();
                        sc.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, timeout));
                        break;
                    case "PAUSE":
                        sc.Pause();
                        sc.WaitForStatus(ServiceControllerStatus.Paused, new TimeSpan(0, 0, 0, timeout));
                        break;
                    case "CONTINUE":
                        sc.Continue();
                        sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeout));
                        break;
                    default:
                        Console.WriteLine("Invalid command value.\n");
                        ShowUsage();
                        return ERROR_INVALID_COMMAND_LINE;
                }
            }
            catch (System.ServiceProcess.TimeoutException)
            {
                Console.WriteLine("Operation timed out.\n");
                return ERROR_COMMAND_TIMEOUT;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("You are not authorized to perform this action.\n");
                return ERROR_NO_ACCESS;
            }
            catch (InvalidOperationException opEx)
            {
                Win32Exception winEx = opEx.InnerException as Win32Exception;
                if (winEx != null)
                {
                    switch (winEx.NativeErrorCode)
                    {
                        case 5: //ERROR_ACCESS_DENIED
                            Console.WriteLine("You are not authorized to perform this action.\n");
                            return ERROR_NO_ACCESS;
                        case 1722: //RPC_S_SERVER_UNAVAILABLE
                            Console.WriteLine("The server is unavailable or does not exist.\n");
                            return ERROR_NO_SERVER;
                        case 1060: //ERROR_SERVICE_DOES_NOT_EXIST
                            Console.WriteLine("The service does not exist.\n");
                            return ERROR_NO_SERVICE;
                        case 1056: //ERROR_SERVICE_ALREADY_RUNNING
                            Console.WriteLine("The service is already running.\n");
                            return ERROR_INVALID_STATE;
                        case 1062: //ERROR_SERVICE_NOT_ACTIVE
                            Console.WriteLine("The service is not running.\n");
                            return ERROR_INVALID_STATE;
                        default:
                            break;
                    }
                }
                Console.WriteLine(opEx.ToString());
                return ERROR_UNSPECIFIED;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return ERROR_UNSPECIFIED;
            }

            return ERROR_SUCCESS;
        }

        private static void ShowUsage()
        {
            Console.WriteLine("SCSync usage:\n");
            Console.WriteLine("SCSync.exe service command <server> <timeout>\n");
            Console.WriteLine("    service   The name of the service upon which the command will act. (Required)");
            Console.WriteLine("    command   The command to execute - one of: start|stop|pause|continue. (Required)");
            Console.WriteLine("    server    The name of the server on which the target service runs. This must start with \\. (Optional)");
            Console.WriteLine("    timeout   The timeout period in seconds in which the command should finish. The default is 30 seconds. (Optional)");
            Console.WriteLine("\n");
        }
    }
}

WaitForStatus只是一个轮询循环,可以在任何其他语言中轻松替换。其余部分只涉及OpenService和ControlService。


7

Eric Falsken的解决方案完美地运行。+1。

但是我想补充一点,timeout命令有时会失败并显示错误信息:"不支持输入重定向,进程立即退出"。

为了解决这个问题,我不得不替换timeout命令:

timeout /t 2 /nobreak >NUL

以下是需要的内容:

ping -n 2 127.0.0.1  1>NUL

5

2011年10月20日更新:我更新了我的代码。在发布之前,我并没有完全调试好它。非常感谢Eric Falsken提供的帮助。他的解决方案非常好。我对Eric的代码进行了微调(顺便提一下,如果你要使用它,请注意几个排版错误)。我添加了日志记录和一些额外的错误检查,以处理Eric没有考虑到的一些情况。由于我最感兴趣的是服务重启(而不仅仅是停止和/或启动),所以我只是在Eric的重启代码的基础上进行了修改。无论如何,我发布了我的版本,希望你喜欢它!

@ECHO off
:: This script originally authored by Eric Falsken http://stackoverflow.com/
:: Revised for by George Perkins 10/20/2011
IF [%1]==[] GOTO Usage
IF [%2]==[] GOTO Usage

:SetLocalVariables
SET /A MyDelay=0 
SET MyHours=%time:~0,2%
IF %MyHours%==0 SET MyHours=00
IF %MyHours%==1 SET MyHours=01
IF %MyHours%==2 SET MyHours=02
IF %MyHours%==3 SET MyHours=03
IF %MyHours%==4 SET MyHours=04
IF %MyHours%==5 SET MyHours=05
IF %MyHours%==6 SET MyHours=06
IF %MyHours%==7 SET MyHours=07
IF %MyHours%==8 SET MyHours=08
IF %MyHours%==9 SET MyHours=09
SET MyMinutes=%time:~3,2%
SET MySeconds=%time:~6,2%
SET MyHundredths=%time:~9,2%
SET MyMonth=%date:~4,2%
SET MyDay=%date:~-7,2%
SET MyCentury=%date:~-4,4%
SET MyTimeStamp=%MyCentury%%MyMonth%%MyDay%%MyHours%%MyMinutes%%MySeconds%
IF "%3" == "" (
         SET MyLog=C:\Temp
   ) ELSE (
         SET MyLog=%3
   ) 
SET MyLogFile=%MyLog%\ServiceRestart%MyTimeStamp%.log
ECHO.
ECHO. >> %MyLogFile%
ECHO ------------- ------------- %MyHours%:%MyMinutes%:%MySeconds%.%MyHundredths% %MyMonth%/%MyDay%/%MyCentury% ------------- ------------- 
ECHO ------------- ------------- %MyHours%:%MyMinutes%:%MySeconds%.%MyHundredths% %MyMonth%/%MyDay%/%MyCentury% ------------- ------------- >> %MyLogFile% 
ECHO Begin batch program %0. 
ECHO Begin batch program %0. >> %MyLogFile%
ECHO Logging to file %MyLogFile%. 
ECHO Logging to file %MyLogFile%. >> %MyLogFile% 
ECHO Attempting to restart service %2 on computer %1.
ECHO Attempting to restart service %2 on computer %1. >> %MyLogFile%

PING -n 1 %1 | FIND "TTL=" >> %MyLogFile%
IF errorlevel 1 IF NOT errorlevel 2 GOTO SystemOffline
SC \\%1 query %2 | FIND "FAILED 1060" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO InvalidServiceName
SC \\%1 query %2 | FIND "STATE" >> %MyLogFile%
IF errorlevel 1 IF NOT errorlevel 2 GOTO SystemOffline

:ResolveInitialState
SET /A MyDelay+=1
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
ECHO Service State is changing, waiting %MyDelay% seconds for service to resolve its state before making changes.
ECHO Service State is changing, waiting %MyDelay% seconds for service to resolve its state before making changes. >> %MyLogFile%
TIMEOUT /t %MyDelay% /nobreak >> %MyLogFile%
GOTO ResolveInitialState

:StopService
SET /A MyDelay=0
ECHO Stopping %2 on \\%1.
ECHO Stopping %2 on \\%1. >> %MyLogFile%
SC \\%1 stop %2 | FIND "FAILED" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO Unstoppable

:StoppingServiceDelay
SET /A MyDelay+=1
IF %MyDelay%==21 GOTO MaybeUnStoppable
ECHO Waiting %MyDelay% seconds for %2 to stop.
ECHO Waiting %MyDelay% seconds for %2 to stop. >> %MyLogFile%
TIMEOUT /t %MyDelay% /nobreak >> %MyLogFile%
:StoppingService
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService
SC \\%1 query %2 | FIND "STATE" | FIND "STOP_PENDING" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppingServiceDelay
GOTO StoppingServiceDelay

:MaybeUnStoppable
:: If we got here we waited approximately 3 mintues and the service has not stopped.
SC \\%1 query %2 | FIND "NOT_STOPPABLE" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO OneLastChance
GOTO Unstoppable 

:OneLastChance
SC \\%1 stop %2 >> %MyLogFile%
SET /A MyDelay+=1
ECHO Waiting %MyDelay% seconds for %2 to stop.
ECHO Waiting %MyDelay% seconds for %2 to stop. >> %MyLogFile%
TIMEOUT /t %MyDelay% /nobreak >> %MyLogFile%
SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO StoppedService
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >> %MyLogFile%
IF errorlevel 1 IF NOT errorlevel 2 GOTO UnknownState
SC \\%1 query %2 | FIND "NOT_STOPPABLE" >> %MyLogFile%
IF errorlevel 0 IF NOT errorlevel 1 GOTO Unstoppable
GOTO StoppingServiceDelay

:StoppedService
ECHO %2 on \\%1 is stopped.
ECHO %2 on \\%1 is stopped. >> %MyLogFile%
GOTO StartService

:StartService
SET /A MyDelay=0 
ECHO Starting %2 on \\%1.
ECHO Starting %2 on \\%1. >> %MyLogFile%
SC \\%1 start %2 >> %MyLogFile%

GOTO StartingService
:StartingServiceDelay
SET /A MyDelay+=1
ECHO Waiting %MyDelay% seconds for %2 to start.
ECHO Waiting %MyDelay% seconds for %2 to start. >> %MyLogFile%
TIMEOUT /t %MyDelay% /nobreak >> %MyLogFile%
:StartingService
SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >> %MyLogFile%
IF errorlevel 1 IF NOT errorlevel 2 GOTO StartingServiceDelay

:StartedService
ECHO %2 on \\%1 is started.
ECHO %2 on \\%1 is started. >> %MyLogFile%
GOTO EndExit

:SystemOffline
ECHO Failure! Server \\%1 or service %2 is not accessible or is offline!
ECHO Failure! Server \\%1 or service %2 is not accessible or is offline! >> %MyLogFile%
ECHO See log file %MyLogFile% for details!
GOTO EndExit

:InvalidServiceName
ECHO Failure! Service %2 is not valid!
ECHO Failure! Service %2 is not valid! >> %MyLogFile%
ECHO See log file %MyLogFile% for details!
GOTO EndExit

:UnknownState
ECHO Failure! Service %2 in an unknown state and cannot be stopped!
ECHO Failure! Service %2 in an unknown state and cannot be stopped! >> %MyLogFile%
ECHO See log file %MyLogFile% for details!
GOTO EndExit

:UnStoppable
ECHO Failure! Service %2 cannot be stopped! Check dependencies or system state.
ECHO Failure! Service %2 cannot be stopped! Check dependencies or system state. >> %MyLogFile%
ECHO See log file %MyLogFile% for details!
GOTO EndExit

:Usage
ECHO Will restart a remote service, waiting for the service to stop/start (if necessary).
ECHO.
ECHO Usage:
ECHO %0 [system name] [service name] [logfile path]
ECHO Example: %0 server1 MyService C:\Temp\Log
ECHO.
GOTO EndExit

:EndExit
ECHO.
ECHO %0 Ended.
ECHO.

4

那么PowerShell和Restart-Service命令呢? :)

Get-Service W3SVC -computer myserver | Restart-Service

不错。记得从管理员启用的 shell 中运行(如果没有提升权限,错误消息不太清楚)。 - matt wilkie
对于 Powershell 2.0,您必须采用一个技巧Get-Service W3SVC -computer myserver | Set-Service -Status Stopped; Get-Service W3SVC -computer myserver | Set-Service -Status Running - sferencik

2

Eric Falsken的脚本非常适合这个目的。但请注意,它们使用timeout命令,该命令仅适用于Vista/Server2003及更高版本。对于XP机器,您可以使用NT资源工具包中的sleep.exe代替。(这应该是对Eric的答案进行评论,但没有足够的声望来执行此操作)。


2

我改进了Eric Falsken的脚本,并由George Perkins修订。

更改:

  • 现在它不仅是一个重新启动脚本。该脚本可以启动、停止和重新启动本地或远程服务;
  • 移除了日志记录(如果您需要,只需通过启动SCRIPT_NAME.bat> logfile.txt来使用);
  • 稀疏优化。

    @ECHO off
    :: 该脚本最初由Eric Falsken编写http://stackoverflow.com/
    :: 由George Perkins于10/20/2011进行修订
    :: 由Armando Contestabile于02/23/2015进行修订
    IF "%1"=="" GOTO Usage
    IF "%2"=="" GOTO Usage
    
    SET ACTION=%1
    SET SERVICENAME=%2
    IF "%3"=="" ( SET SYSTEMNAME=%COMPUTERNAME% ) ELSE ( SET SYSTEMNAME=%3 )
    IF "%ACTION%" == "stop" ( SET ACTION=STOP ) ELSE IF "%ACTION%" == "STOP" ( SET ACTION=STOP ) ELSE IF "%ACTION%" == "start" ( SET ACTION=START ) ELSE IF "%ACTION%" == "START" ( SET ACTION=START ) ELSE IF "%ACTION%" == "restart" ( SET ACTION=RESTART ) ELSE IF "%ACTION%" == "RESTART" ( SET ACTION=RESTART ) ELSE GOTO Usage SET STATE= SET CURRENT_STATUS= SET /A DEFAULT_DELAY=5 SET /A SLEEP_COUNT=0 SET /A RESTARTED=0 SET /A MAX_WAIT_PERIODS=5
    ECHO. ECHO 正在尝试在计算机%SYSTEMNAME%上%ACTION%服务%SERVICENAME%。
    PING -n 1 %SYSTEMNAME% | FIND "TTL=" >nul 2>&1 IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 ( ECHO 失败!服务器\\%SYSTEMNAME%或服务%SERVICENAME%不可访问或离线! EXIT /B 1 ) SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "FAILED 1060" >nul 2>&1 IF ERRORLEVEL 0 IF NOT ERRORLEVEL 1 ( ECHO 失败!服务%SERVICENAME%无效! EXIT /B 2 ) SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "STATE" >nul 2>&1 IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 ( ECHO 失败!服务器\\%SYSTEMNAME%或服务%SERVICENAME%不可访问或离线! EXIT /B 3 )
    :Dispatch FOR /f "tokens=*" %%i IN ('SC \\%SYSTEMNAME% query %SERVICENAME% ^| FIND "STATE"') DO SET STATE=%%i
    ECHO %STATE% | FINDSTR /C:"1" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOPPED ECHO %STATE% | FINDSTR /C:"2" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=START_PENDING ECHO %STATE% | FINDSTR /C:"3" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOP_PENDING ECHO %STATE% | FINDSTR /C:"4" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=RUNNING ECHO %STATE% | FINDSTR /C:"5" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=CONTINUE_PENDING ECHO %STATE% | FINDSTR /C:"6" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSE_PENDING ECHO %STATE% | FINDSTR /C:"7" >nul IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSED
    ECHO 服务的当前状态为%CURRENT_STATUS%
    IF NOT "%CURRENT_STATUS%"=="RUNNING" IF NOT "%CURRENT_STATUS%"=="STOPPED" IF NOT "%CURRENT_STATUS%"=="PAUSED" ( IF "%SLEEP_COUNT%"=="%MAX_WAIT_PERIODS%" ( ECHO 服务状态不会改变。脚本执行被取消。 EXIT /B 4 ) ECHO 服务状态正在改变,等待%DEFAULT_DELAY%秒... SLEEP %

我收到了“'SLEEP' 不是内部或外部命令”的错误提示。 - kofifus
你使用的是哪个Windows版本? - Armando Contestabile

1
我对脚本进行了微小的更改,以便在Windows 10或类似版本下运行。命令“sleep”被替换为命令“timeout”。
@ECHO off
:: This script originally authored by Eric Falsken http://stackoverflow.com/
:: Revised by George Perkins 10/20/2011
:: Revised by Armando Contestabile 02/23/2015
:: Revised by Sascha Jelinek 11/13/2020
IF "%1"=="" GOTO Usage
IF "%2"=="" GOTO Usage

SET ACTION=%1
SET SERVICENAME=%2

IF "%3"=="" (
    SET SYSTEMNAME=%COMPUTERNAME%
) ELSE (
    SET SYSTEMNAME=%3
)

IF "%ACTION%" == "stop" (
    SET ACTION=STOP
) ELSE IF "%ACTION%" == "STOP" (
    SET ACTION=STOP
) ELSE IF "%ACTION%" == "start" (
    SET ACTION=START
) ELSE IF "%ACTION%" == "START" (
    SET ACTION=START
) ELSE IF "%ACTION%" == "restart" (
    SET ACTION=RESTART
) ELSE IF "%ACTION%" == "RESTART" (
    SET ACTION=RESTART
) ELSE GOTO Usage

SET STATE=
SET CURRENT_STATUS=
SET /A DEFAULT_DELAY=5
SET /A SLEEP_COUNT=0
SET /A RESTARTED=0
SET /A MAX_WAIT_PERIODS=5

ECHO.
ECHO Attempting to %ACTION% service %SERVICENAME% on computer %SYSTEMNAME%.

PING -n 1 %SYSTEMNAME% | FIND "Antwort von" >nul 2>&1
IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 (
    ECHO Failure!! Server \\%SYSTEMNAME% or service %SERVICENAME% is not accessible or is offline!
    EXIT /B 1
)
SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "FAILED 1060" >nul 2>&1
IF ERRORLEVEL 0 IF NOT ERRORLEVEL 1 (
    ECHO Failure! Service %SERVICENAME% is not valid!
    EXIT /B 2
)
SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "STATE" >nul 2>&1
IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 (
    ECHO Failure! Server \\%SYSTEMNAME% or service %SERVICENAME% is not accessible or is offline!
    EXIT /B 3
)

:Dispatch
FOR /f "tokens=*" %%i IN ('SC \\%SYSTEMNAME% query %SERVICENAME% ^| FIND "STATE"') DO SET STATE=%%i

ECHO %STATE% | FINDSTR /C:"1" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOPPED
ECHO %STATE% | FINDSTR /C:"2" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=START_PENDING
ECHO %STATE% | FINDSTR /C:"3" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOP_PENDING
ECHO %STATE% | FINDSTR /C:"4" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=RUNNING
ECHO %STATE% | FINDSTR /C:"5" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=CONTINUE_PENDING
ECHO %STATE% | FINDSTR /C:"6" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSE_PENDING
ECHO %STATE% | FINDSTR /C:"7" >nul
IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSED

ECHO Current status of service is %CURRENT_STATUS%

IF NOT "%CURRENT_STATUS%"=="RUNNING" IF NOT "%CURRENT_STATUS%"=="STOPPED" IF NOT "%CURRENT_STATUS%"=="PAUSED" (
    IF "%SLEEP_COUNT%"=="%MAX_WAIT_PERIODS%" (
        ECHO Service state won't change. Script exececution is canceled.
        EXIT /B 4
    )
    ECHO Service State is changing, waiting %DEFAULT_DELAY% seconds...
    TIMEOUT /t %DEFAULT_DELAY% /NOBREAK
    SET /A SLEEP_COUNT+=1
    GOTO Dispatch
)

IF "%ACTION%"=="START" (
    IF "%CURRENT_STATUS%"=="RUNNING" (
        ECHO Service %SERVICENAME% is running.
        GOTO EndExit
    ) ELSE (
        GOTO StartService
    )
) ELSE IF "%ACTION%"=="RESTART" (
    IF "%CURRENT_STATUS%"=="RUNNING" (
        IF %RESTARTED%==1 (
            ECHO Service %SERVICENAME% restarted.
            GOTO EndExit
        )
        SET /A SLEEP_COUNT=0
        GOTO StopService
    ) ELSE (
        SET /A RESTARTED=1
        GOTO StartService
    )
) ELSE IF "%ACTION%"=="STOP" (
    IF "%CURRENT_STATUS%"=="STOPPED"  (
        ECHO Service %SERVICENAME% is stopped.
        GOTO EndExit
    ) ELSE (
        GOTO StopService
    )
)

:StartService
ECHO Starting %SERVICENAME% on \\%SYSTEMNAME%
SC \\%SYSTEMNAME% start %SERVICENAME% >nul 2>&1
SET SLEEP_COUNT=0
GOTO Dispatch

:StopService
ECHO Stopping %SERVICENAME% on \\%SYSTEMNAME%
SC \\%SYSTEMNAME% stop %SERVICENAME% >nul 2>&1
SET SLEEP_COUNT=0
GOTO Dispatch

:Usage
ECHO This script can start/stop/restart a local or remote service, waiting for the service to stop/start ^(if necessary^).
ECHO.
ECHO Usage:
ECHO %0 ^<start^|stop^|restart^> ^<SERVICE^> [SYSTEM]
ECHO.
ECHO If no SYSTEM is provided, the script attempts to execute on the local system.
EXIT /B 5

:EndExit
ECHO.
EXIT /B 0

0

NET START 和 NET STOP 在服务成功启动或停止后才应该返回。


1
我可以确认,当远程终止服务时,它们确实会在服务停止之前返回。我每天都这样做好几次。 - Jason Jackson
有趣。我以为他们会等到服务向SCM返回了SERVICE_STARTED或SERVICE_STOPPED代码。 - Joe
让我澄清一下之前的评论。根据我的经验,Windows可能会认为服务已经停止,但实际上它仍在关闭中。我不知道这是因为服务行为不当还是常规操作。我遇到过不止一次。我不会指望服务在停止后立即被关闭,也肯定不会指望服务在启动后立即开放。 - Jason Jackson
服务可以告诉Windows它想要什么;它可以在告诉Windows它已经“启动”之后延迟执行,也可以在告诉Windows它已经“停止”之后进行其他拆卸。 依我看,这种做法不好,但完全可行。 - Joe
1
一个更正:根据文档,一旦状态设置为已停止,SCM 可以在任何时候终止服务。根据我的经验,这通常会立即发生,也就是说,对 SetServiceStatus 的调用根本不会返回。因此,任何必要的拆除都需要在状态设置为已停止之前进行。(当多个服务共享一个进程时,可能不适用,但我怀疑当最后一个服务停止时也会适用。) - Harry Johnston

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