• 太阳当空照-Windows服务化方式脚本封装sc指令


    前言

    接过上一章节,sc.exe进行Windows中,进行系统程序服务化的操作方式不难发现,直接进行基础工具服务化操作,一个是人狠话不多,出错不好说,一个就是不太优雅,每回都需要自己手动去输入配置信息,例如:sc [Service name] 程序路径,服务方式等内容,啥也别说,改成脚本引导,交互式输入,以下是脚本的几个需求:

    • 启动脚本出现输入引导信息

    • 输入相关服务配置

    • 完成对目标程序服务操作(注册、启动、停止和删除)

    ggcy-blog-service-bat-header

    准备作业

    脚本是啥?在系统中又叫批处理脚本,用于实现系统使用过程中,通过系统Dos指令实现部分功能需求的可执行文件,相关交互指令被称之为批处理脚本,此处简称脚本,在Windows系统中,脚本文件常见以.bat.cmd作为文件后缀

    要写当然啥不会肯定不行,先简单学一学指令,笔者整理了相关的基本语法,如下:

    变量

    变量的主要目的是为了接受在批处理文件外部接受输入值和处理文件内部对变量的设置和使用

    blog-jrz-bat-var

    系统变量

    %CD% 获取当前路径
    %PATH% 获取命令搜索路径/全局环境变量中的PATH
    %DATE% 获取当前日期
    %TIME% 获取当前时间
    %ERRORLEVEL% 获取上一个命令的执行结果码
    

    输出内容echo

    >echo %DATE%
    2021/07/16 周五
    

    设置变量(局部)set

    set 变量名称=变量值
    
    >set test=12
    >echo %test%
    12
    

    基本指令

    以下介绍仅仅包含当前需要实现的批处理文件需要用到的指令,并不是完整指令细化,具体参考可以阅读对应的微软文档参考链接

    blog-jrz-bat-cmd

    注释

    ::注释内容
    

    输入

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1

    set

    显示、设置或删除 cmd.exe 环境变量。 如果不使用参数,则 set 将 显示当前环境变量设置

    set [<variable>=[<string>]]
    set [/p] <variable>=[<promptString>]
    set /a <variable>=<expression>
    

    <variable>指定要修改或设置的环境变量名称

    <string>实际需要给变量设置的变量值

    >set test=aaa
    >echo %test%
    aaa
    

    /p将用户输入行作为对应需要设置的变量的值

    创建一个setcase.batCHCP 65001目的是为了设置当前cmd窗口的当前代码页设置为utf-8,同时把bat脚本编码设置为utf-8内容如下:

    @echo off
    CHCP 65001
    set /p uname=请输入用户名称:
    echo 用户名:%uname%
    

    执行sctest.bat

    >sctest.bat
    Active code page: 65001
    请输入用户名称:张珊
    用户名:张珊
    

    需要注意的是,如果执行后输出内容为中文乱码,那么极为可能是,当前bat的文件编码和cmd窗口编码不一致造成,可通过CHCP 编码进行编码统一,上述案例设置的是65001表示输出脚本当前窗口编码为utf-8,和bat的编码保持一致

    输出

    echo

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/echo

    显示消息或打开或关闭命令回显功能。 如果不使用参数, echo 将显示当前的回显设置,默认为on

    echo [<message>]
    echo [on | off]
    

    显示信息(输出信息)

    >echo Hello World
    echo Hello World
    

    控制是否显示命令提示符,在dos环境下直接执行该指令有效,为防止在bat文件中显示

    echo [on | off]
    

    执行echo用于显示当前回显状态

    >echo 
    ECHO 处于打开状态。
    

    创建一个echo.bat文件内容如下:

    echo hellw world
    pause 
    

    保存后,双击执行,输出结果如下:

    bat文件路径>echo hellw world
    hellw world
    bat文件路径>pause
    

    关闭回显,防止批处理文件中的所有命令 (包括 echo off 命令) 在屏幕上显示在批处理文件类型的第一行,echo.bat修改如下:

    @echo off
    echo hellw world
    pause
    

    保存后,直接双击bat执行效果如下:

    hellw world
    请按任意键继续. . .
    

    暂停

    pause

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/pause

    暂停批处理程序的执行,并显示提示, Press any key to continue . . .,按任意键继续执行后续的执行

    >pause
    请按任意键继续. . .
    

    退出

    exit

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/exit

    退出命令解释器或当前批处理脚本

    exit [/b] [<exitcode>]
    

    /b为退出当前批处理脚本,而不是退出cmd.exe,如果从批处理脚本外部执行或直接执行批处理脚本,则退出 cmd.exe

    <exitcode>指定数值, 如果指定了 /b ,则 ERRORLEVEL 环境变量设置为该数字; 如果要退出命令解释器,则进程退出代码将设置为该数字

    批处理脚本exit.bat内容如下:

    @echo off
    echo hellw world
    exit /b  
    

    直接双击运行bat,看似无反应,实际是脚本执行结束后,退出了了

    通过命令提示符切换到改exit.bat所在目录下,执行exit.bat,结果输出如下:

    >sctest.bat
    hellw world
    >
    

    命令提示符cmd.exe并未退出

    判定

    if

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/if

    在批处理程序中执行条件处理

    if [not] ERRORLEVEL <number> <command> [else <expression>]
    if [not] <string1>==<string2> <command> [else <expression>]
    if [not] exist <filename> <command> [else <expression>]
    

    启用了命令扩展,语法如下:

    if [/i] <string1> <compareop> <string2> <command> [else <expression>]
    if cmdextversion <number> <command> [else <expression>]
    if defined <variable> <command> [else <expression>]
    

    not当条件为false,对应判定语句才能够执行内部的相关操作

    >if not false==true echo the condition is false
    the condition is false
    

    errorlevel 表示cmd.exe上一个执行程序的返回的退出代码对应的数字

    >echo %errorlevel%
    0
    >if not %errorlevel%==1 echo the condition is false
    the condition is false
    

    exist判定特定路径文件是否存在,存在则返回true,命令提示符工作目录下存在文件sctest.bat

    >if exist sctest.bat  echo the condition is true
    the condition is true
    

    else <express>不符合if条件判定时的其他情况,执行对应的相关操作语句,执行语句需要用()进行囊括在一个块中,否则else将执行无效

    >if exist echo.bat  (echo the condition is true) else (echo the condition is false)
    the condition is false
    

    执行批处理文件

    call

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/call

    从一个批处理程序调用另一个批处理程序,而不停止父批处理程序,需要注意的是该指令在命令提示符中执行无效

    call [drive:][path]<filename> [<batchparameters>] [:<label> [<arguments>]]
    

    [<drive>:][<path>]<filename>需要指定的bat的路径和名称,需要指明批处理文件的文件后缀,name.batname.cmd

    <batchparameters>指定批处理程序所需的任何命令行信息

    示例:

    创建一个child.bat,内容如下:

    echo helloworld~
    

    同目录下,再创建一个parent.bat,调用child.bat

    @echo off
    CHCP 65001
    echo 开始调用子批量处理文件
    call child.bat 
    echo %errorlevel%
    echo 完成子批量处理文件
    pause
    

    执行parent.bat

    >parent.bat
    Active code page: 65001
    开始调用子批量处理文件
    helloworld~
    完成子批量处理文件
    Press any key to continue . . .
    

    :<label>指定批处理跳转的标签名称

    <arguments>指定要传递给批处理程序的新实例(从开始)的命令行信息 :<label>

    批处理参数

    %~1		展开 %1 并删除周围的引号
    %~f1	将 %1 扩展到完全限定的路径
    

    示例:

    创建文件callself.bat,文件内容如下,以执行方式跳转标签并传递参数

    @echo off
    CHCP 65001
    call :labelname "参数01"
    echo 1
    echo 2
    if %errorlevel%==1 (
    exit /b
    )
    :labelname
    echo 3
    echo 4
    exit /b 1
    

    输出结果

    >callself.bat
    Active code page: 65001
    3
    参数01
    4
    1
    2
    

    定向跳转

    goto

    cmd.exe定向到批处理程序中带标签的行

    参考链接:https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/goto

    goto <label>
    

    <label>指定一个文本字符串,该字符串用作批处理程序中的标签

    更改当前工作路径

    获取当前bat所在路径,切换该路径为工作目录

    cd /d %~dp0
    

    实现逻辑

    将脚本文件作为一个简单的指令入口,将指令依据实际的操作标识字符串进行访问到对应的子批量处理文件,例如输入install就开始执行安装服务的安装引导,其他操作同理,由于脚本是由多个批处理文件组成,所有批处理结构如下:

    blog-jrz-bat-group

    判定服务是否存在

    查询服务

    >sc query servicename
    

    当服务不存在时,输出如下:

    >sc query sct
    [SC] EnumQueryServicesStatus:OpenService FAILED 1060:
    
    The specified service does not exist as an installed service.
    

    若只是需要获取错误码,则设置输出为nul

    >sc query sct >nul
    >
    >echo %errorlevel%
    1060
    

    因此可以采用上述方式对服务是否安装进行判定sc query servicename >nul

    创建isexist.bat作为判定服务脚本

    @echo off
    ::CHCP 65001
    ::判定服务是否存在
    set name=%~1
    ::echo %name%
    sc query %name% >nul
    exit /b
    

    输入判定

    @echo off
    ::CHCP 65001
    ::接收外部参数
    set option=%~1
    :setname
    ::输入服务名称
    set /p servicename=请输入需要%option%的服务名称:
    ::判定服务名称是否为空
    if "%servicename%"=="" (
    echo 服务名称不能为空
    goto setname
    )
    
    call isexist.bat %servicename%
    
    if %errorlevel%==1060 (
    echo 服务名称不存在或服务未安装
    goto setname
    )
    

    安装

    创建一个install.bat文件,具体内容如下:

    @echo off
    CHCP 65001
    ::添加跳转标签
    :path
    set /p startup=请输入服务目标程序路径:
    if not exist %startup% (
    echo %startup%路径不存在,请重新输入
    goto :path
    )
    
    ::通过跳转传递路径参数
    call :filename %startup%
    
    ::判定执行是否合理
    if	%errorlevel%==1 (
    exit /b
    )
    
    :filename
    ::默认服务名
    set file_name=%~n1
    ::echo %file_name%
    :setname
    ::配置服务名称
    set /p servicename=请输入服务名称(默认%file_name%):
    ::配置服务显示名称
    if "%servicename%"=="" (
    set servicename=%file_name%
    )
    ::判定服务名称是否已存在
    call isexist.bat %servicename%
    if %errorlevel%==1060 (
    goto setname
    )
    ::配置服务描述
    set /p discription=请输入服务描述(默认%file_name%):
    if "%discription%"=="" (
    set discription=%file_name%
    )
    ::配置服务启动模式	
    set mode=demand
    ::配置服务启动模式	
    set /p servicemode=请输入服务启动模式(默认%mode%手动):
    if "%servicemode%"=="" (
    set servicemode=%mode%
    )
    ::安装服务
    sc create %servicename% binPath= %startup% start= %servicemode% DisplayName= %servicename%
    ::修改描述
    sc description %servicename% %discription%
    exit /b 1
    

    需要注意的是,if条件语句执行体如果包含多条执行语句时,需要用使用()进行包裹,同时,与条件语句之间需要用空格进行分隔,同时必须是同行中添加(,否则将出现异常The syntax of the command is incorrect.这个常规异常

    启动

    @echo off
    ::CHCP 65001
    set option="启动"
    :::setname
    ::::输入服务名称
    ::set /p servicename=请输入需要%option%的服务名称:
    ::::判定服务名称是否为空
    ::if "%servicename%"=="" (
    ::echo 服务名称不能为空
    ::goto setname
    ::)
    
    call input.bat %option%
    
    sc start %servicename%
    
    exit /b 0
    

    停止

    @echo off
    ::CHCP 65001
    set option="停止"
    
    call input.bat %option%
    
    sc stop %servicename%
    
    exit /b 0
    

    卸载

    @echo off
    ::CHCP 65001
    set option="卸载"
    
    call input.bat %option%
    ::执行服务删除操作
    sc delete %servicename%
    
    exit /b 0
    

    组合

    @echo off
    CHCP 65001
    ::获取参数
    set cmd=%1
    ::判定指令
    if "%cmd%"=="" (
    echo 输入%cmd%无效
    goto help
    )
    
    ::安装服务
    if "%cmd%"=="install" (
    call install.bat
    goto finish
    )
    ::卸载服务
    if "%cmd%"=="uninstall" (
    call uninstall.bat
    goto finish
    )
    ::启动服务
    if "%cmd%"=="start" (
    call start.bat
    goto finish
    )
    ::停止服务
    if "%cmd%"=="stop" (
    call stop.bat
    goto finish
    )
    else (
    ::帮助指令
    echo 输入%cmd%无效
    :help
    echo 输入help查看对应指令
    echo scutil [command]
    echo [command]如下:
    echo install	安装服务
    echo uninstall	卸载服务
    echo start		启动服务
    echo stop		停止服务
    )
    ::退出执行
    :finish
    exit /b 0
    

    测试运行

    管理员启动cmd,切换目录到scutil.bat对应的目录,执行安装指令,当前服务测试路径为E:StudyServerssctestsctest.exesctest.exe为上一个章节的服务程序,完全符合windows服务化要求

    >scutil install
    Active code page: 65001
    请输入服务目标程序路径:"E:StudyServerssctestsctest.exe"
    请输入服务名称(默认sctest):
    请输入服务描述(默认sctest):
    请输入服务启动模式(默认demand手动):
    [SC] CreateService SUCCESS
    [SC] ChangeServiceConfig2 SUCCESS
    

    查看本地服务sc query sctest

    >sc query sctest
    SERVICE_NAME: sctest
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 1  STOPPED
            WIN32_EXIT_CODE    : 1077  (0x435)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
    

    运行程序

    >scutil start
    Active code page: 65001
    请输入需要启动的服务名称:sctest
    
    SERVICE_NAME: sctest
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 2  START_PENDING
                                    (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x7d0
            PID                : 14244
            FLAGS              :
    

    对应目录下,生成一个当前日期的内容输出文件,内容为不断追加的时间值

    blog-jrz-bat-run

    当前时间:Service Start
    当前时间:22:03:56
    当前时间:22:03:57
    当前时间:22:03:58
    当前时间:22:03:59
    当前时间:22:04:00
    当前时间:22:04:01
    当前时间:22:04:02
    当前时间:22:04:03
    当前时间:22:04:04
    当前时间:22:04:05
    

    停止程序

    >scutil stop
    Active code page: 65001
    请输入需要停止的服务名称:sctest
    
    SERVICE_NAME: sctest
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 3  STOP_PENDING
                                    (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
            
    >sc query sctest
    
    SERVICE_NAME: sctest
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 1  STOPPED
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
    

    卸载程序

    >scutil uninstall
    Active code page: 65001
    请输入需要卸载的服务名称:sctest
    [SC] DeleteService SUCCESS
    
    >sc query sctest
    [SC] EnumQueryServicesStatus:OpenService FAILED 1060:
    
    The specified service does not exist as an installed service
    

    以上就是本章对于将sc.exe实现脚本化,进而实现程序服务化的交互式操作

    总结

    这些脚本虽然看似鸡肋,只是将指令进行了二次封装,实际上,这是笔者第一个比较系统化的对脚本的实践和应用,能够将这些指令以类似简单编程的方式进行处理也是一种经验的积累,后续将继续讲解笔者接触到的Windows服务化的其他工具的使用和思考

    • instsrvsrvany
    • Winsw
    • Nssm

    获取上述内容中的服务测试源码项目,可关注私信或直接评论回复【sc.bat

  • 相关阅读:
    switch语句
    switch语句
    if语句三种格式
    dowhile语句
    if语句三种格式
    if语句配对
    ansible
    linux系统中网站服务程序(web服务/httpd服务)
    ansible
    ansible
  • 原文地址:https://www.cnblogs.com/guanguanchangyu/p/15037606.html
Copyright © 2020-2023  润新知