【问题标题】:Stopping/Starting a remote Windows service and waiting for it to open/close停止/启动远程 Windows 服务并等待它打开/关闭
【发布时间】:2009-09-10 13:38:00
【问题描述】:

this question 的最佳答案告诉我如何停止/启动远程服务。伟大的。 现在,我只需要等待实际的停止/启动完成。所以,我正在寻找的是一个 dos 命令:

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

【问题讨论】:

  • 我自己很想知道这一点。我不相信 SC 命令是可能的,它本质上是异步的。当服务状态从 STOPPING 变为 STOPPED 以及从 STARTING 变为 STARTED 时,您可能必须将该命令与服务状态的 ping 联系起来。

标签: windows windows-services


【解决方案1】:

我创建了一组使用 sc.exe 来执行此操作的批处理脚本。它们附在下面。要运行这些脚本,您应该是在目标计算机上具有管理权限并从属于同一域成员的计算机上运行此脚本的用户。可以将其设置为能够从域外部运行(例如从 VPN),但是需要通过涉及防火墙、DCOM 和安全凭证的许多安全层。

这些天,我要找出 PowerShell 的等效项,这应该会容易得多。

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

【讨论】:

  • 请注意,超时命令仅在 Vista 和 Server 2003(及更新版本)中可用。在 Windows XP 上,使用 NT Resource Kit 中的 SLEEP 命令。 (感谢詹姆斯)
  • 另请注意,用于检查服务器可用性的 ping 命令假定服务器在 IPv4 上。包含“-4”开关以强制 ping 使用 IPv4 可能会更好。
  • 我希望我有不止一张赞成票。这太棒了。
  • @EricFalsken - 您(否则很棒)脚本中的“系统离线”检测位存在 ipv6 地址问题,因为 ping 不会打印出 ipv6 地址的 TTL。添加 -4 强制 ipv4 ping,但这并不总是可以做到
  • 好剧本!我发现我确实需要 ping -4 并用 ping 替换超时,如下所示。此外,我认为最好删除 sc 启动/停止命令的 >NULL,这样您就可以实际看到任何异常消息。我还在那里添加了“IF errorlevel 1 EXIT 1”,以便脚本在访问被拒绝等错误时停止,而不是永远等待。
【解决方案2】:

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 - 只需将代码放入扩展名为“.ps1”的文件中,然后从 Poweshell 控制台或 ISE 运行即可。
【解决方案3】:

我实际上从未见过专门执行此操作的东西,但是在 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。

【讨论】:

    【解决方案4】:

    Eric Falsken 的解决方案非常有效。 +1。

    但我想补充一点,超时命令有时会因错误而失败: "不支持输入重定向,立即退出进程"

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

    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.
      

      【讨论】:

        【解决方案6】:

        PowerShell 和Restart-Service commandlet 怎么样? :)

        Get-Service W3SVC -computer myserver | Restart-Service
        

        【讨论】:

        • 不错。请记住从启用管理员的 shell 运行(未提升时的错误消息不是很清楚)。
        • 对于 Powershell 2.0,您必须求助于 trick: Get-Service W3SVC -computer myserver | Set-Service -Status Stopped; Get-Service W3SVC -computer myserver | Set-Service -Status Running
        【解决方案7】:

        Eric Falsken 的脚本非常适合此用途。但请注意,他们使用的 timeout 命令仅在 Vista/Server2003 和更新版本中可用。对于 XP 机器,您可以改用 NT Resource Kit 中的 sleep.exe。 (这应该是对 Eric 答案的评论,但没有足够的代表这样做)。

        【讨论】:

          【解决方案8】:

          我改进了 Eric Falsken 的剧本,并由 Gerorge Perkins 修改。

          变化:

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

            @ECHO 关闭
            :: 这个脚本最初由 Eric Falsken 创作 http://stackoverflow.com/
            :: George Perkins 修订 2011 年 10 月 20 日
            :: 由 Armando Contestabile 修订,2015 年 2 月 23 日
            IF "%1"=="" GOTO 用法
            IF "%2"=="" GOTO 用法
            
            设置操作=%1
            设置服务名称=%2
            
            如果 "%3"=="" (
                设置系统名称=%计算机名称%
            ) 别的 (
                设置系统名称=%3
            )
            
            IF "%ACTION%" == "停止" (
                设置动作=停止
            ) ELSE IF "%ACTION%" == "停止" (
                设置动作=停止
            ) ELSE IF "%ACTION%" == "开始" (
                设置动作=开始
            ) ELSE IF "%ACTION%" == "开始" (
                设置动作=开始
            ) ELSE IF "%ACTION%" == "重启" (
                设置动作=重启
            ) ELSE IF "%ACTION%" == "RESTART" (
                设置动作=重启
            ) ELSE GOTO 用法
            
            设置状态=
            设置当前状态=
            SET /A DEFAULT_DELAY=5
            SET /A SLEEP_COUNT=0
            设置 /A 重新启动 = 0
            SET /A MAX_WAIT_PERIODS=5
            
            回声。
            ECHO 正在计算机 %SYSTEMNAME% 上尝试 %ACTION% 服务 %SERVICENAME%。
            
            PING -n 1 %系统名称% |查找“TTL=”>nul 2>&1
            如果错误级别 1 如果不是错误级别 2 (
                回声失败!服务器 \\%SYSTEMNAME% 或服务 %SERVICENAME% 不可访问或处于脱机状态!
                退出/B 1
            )
            SC \\%SYSTEMNAME% 查询 %SERVICENAME% |查找“失败 1060”>nul 2>&1
            如果错误级别 0 如果不是错误级别 1 (
                回声失败!服务 %SERVICENAME% 无效!
                出口/B 2
            )
            SC \\%SYSTEMNAME% 查询 %SERVICENAME% |查找“状态”>nul 2>&1
            如果错误级别 1 如果不是错误级别 2 (
                回声失败!服务器 \\%SYSTEMNAME% 或服务 %SERVICENAME% 不可访问或处于脱机状态!
                出口/B 3
            )
            
            :派遣
            FOR /f "tokens=*" %%i IN ('SC \\%SYSTEMNAME% query %SERVICENAME% ^| FIND "STATE"') DO SET STATE=%%i
            
            回声%STATE% | FINDSTR /C:"1" >nul
            如果 %ERRORLEVEL%==0 设置当前状态=已停止
            回声%STATE% | FINDSTR /C:"2" >nul
            如果 %ERRORLEVEL%==0 设置 CURRENT_STATUS=START_PENDING
            回声%STATE% | FINDSTR /C:"3" >nul
            如果 %ERRORLEVEL%==0 设置 CURRENT_STATUS=STOP_PENDING
            回声%STATE% | FINDSTR /C:"4" >nul
            如果 %ERRORLEVEL%==0 设置 CURRENT_STATUS=RUNNING
            回声%STATE% | FINDSTR /C:"5" >nul
            IF %ERRORLEVEL%==0 SET CURRENT_STATUS=CONTINUE_PENDING
            回声%STATE% | FINDSTR /C:"6" >nul
            如果 %ERRORLEVEL%==0 设置 CURRENT_STATUS=PAUSE_PENDING
            回声%STATE% | FINDSTR /C:"7" >nul
            如果 %ERRORLEVEL%==0 设置当前状态=暂停
            
            ECHO 当前服务状态为 %CURRENT_STATUS%
            
            如果不是 "%CURRENT_STATUS%"=="RUNNING" 如果不是 "%CURRENT_STATUS%"=="STOPPED" 如果不是 "%CURRENT_STATUS%"=="PAUSED" (
                IF "%SLEEP_COUNT%"=="%MAX_WAIT_PERIODS%" (
                    ECHO 服务状态不会改变。脚本执行被取消。
                    出口/B 4
                )
                ECHO 服务状态正在改变,等待 %DEFAULT_DELAY% 秒...
                睡眠 %DEFAULT_DELAY%
                SET /A SLEEP_COUNT+=1
                转到调度
            )
            
            如果 "%ACTION%"=="开始" (
                IF "%CURRENT_STATUS%"=="RUNNING" (
                    ECHO 服务 %SERVICENAME% 正在运行。
                    GOTO EndExit
                ) 别的 (
                    GOTO 启动服务
                )
            ) ELSE IF "%ACTION%"=="RESTART" (
                IF "%CURRENT_STATUS%"=="RUNNING" (
                    如果%重新启动%==1 (
                        ECHO 服务 %SERVICENAME% 重新启动。
                        GOTO EndExit
                    )
                    SET /A SLEEP_COUNT=0
                    GOTO 停止服务
                ) 别的 (
                    设置 /A 重新启动 = 1
                    GOTO 启动服务
                )
            ) ELSE IF "%ACTION%"=="STOP" (
                如果 "%CURRENT_STATUS%"=="已停止" (
                    ECHO 服务 %SERVICENAME% 已停止。
                    GOTO EndExit
                ) 别的 (
                    GOTO 停止服务
                )
            )
            
            :启动服务
            ECHO 在 \\%SYSTEMNAME% 上启动 %SERVICENAME%
            SC \\%SYSTEMNAME% 启动 %SERVICENAME% >nul 2>&1
            SET SLEEP_COUNT=0
            转到调度
            
            :停止服务
            ECHO 在 \\%SYSTEMNAME% 上停止 %SERVICENAME%
            SC \\%SYSTEMNAME% 停止 %SERVICENAME% >nul 2>&1
            SET SLEEP_COUNT=0
            转到调度
            
            :用法
            ECHO 这个脚本可以启动/停止/重启本地或远程服务,等待服务停止/启动^(如果需要的话^)。
            回声。
            回声用法:
            ECHO %0 ^ ^ [SYSTEM]
            回声。
            ECHO 如果未提供 SYSTEM,则脚本将尝试在本地系统上执行。
            出口/B 5
            
            :结束退出
            回声。
            退出 /B 0
            

          【讨论】:

          • 我得到“'SLEEP' 未被识别为内部或外部命令”
          • 您使用的是哪个 Windows 版本?
          【解决方案9】:

          我对脚本进行了小幅更改,使其在 Windows 10 或类似版本下运行。 命令“睡眠”被替换为命令“超时”。

          @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
          

          【讨论】:

            【解决方案10】:

            NET START 和 NET STOP 在服务指示服务已成功启动或停止之前不应返回。

            【讨论】:

            • 我可以确认他们确实返回之前远程杀死服务时服务已停止。我每天这样做几次。
            • 有趣。我以为他们会等到服务向 SCM 指示 SERVICE_STARTED 或 SERVICE_STOPPED 代码。
            • 让我澄清一下之前的评论。根据我的经验,Windows 可能认为该服务已停止,但实际上它仍在拆除。我不知道这是因为服务行为不当还是一切照旧。我不止一次遇到它。我不会指望服务在停止后立即被拆除,当然也不会指望服务在启动后立即开放。
            • 服务可以告诉Windows任何它想要的;它可以在告诉 Windows 已经“开始”之后延迟执行,它可以在告诉 Windows 已经“停止”之后进行额外的拆卸。恕我直言,形式不好,但完全有可能。
            • 一个更正:根据文档,一旦状态设置为停止,SCM 可能随时终止服务。根据我的经验,这通常会立即发生,即对 SetServiceStatus 的调用永远不会返回。因此,任何必要的拆卸都需要在状态设置为停止之前 发生。 (当多个服务共享一个进程时,这可能不适用,但我怀疑它在最后一个服务停止时会适用。)
            【解决方案11】:

            我不相信你可以用直接的 dos 命令来做到这一点。您可以查看Code Project 或其他类似网站,看看是否已经有针对此问题的自定义解决方案。

            如果没有,您可以编写一个辅助 Windows 服务,通过 WCF 端点公开启动/停止功能来为您执行此操作。要远程访问此辅助服务,您可以编写一个简单的控制台应用程序连接到此服务以启动/停止相关的 Windows 服务。由于它是一个控制台应用程序,它会模仿从命令行工作的期望行为,直到完成(或发生错误)才返回。这不是您正在寻找的直接、简单的解决方案,但我会将它扔在那里以供考虑。

            【讨论】:

            • 这对于一个可以使用简单的 bat 脚本解决的任务来说太过分了......记住 KISS 原则......
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-11-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多