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

美男子玩编程

共 13242字,需浏览 27分钟

 ·

2021-08-23 01:37

点击上方蓝色字体,关注我们


1


前言


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

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

  • 输入相关服务配置

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



2


准备作业


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


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

变量

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

系统变量

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

输出内容echo

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

设置变量(局部)set

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



3


基本指令


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

注释

 ::注释内容

输入

参考链接: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



4


实现逻辑


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

判定服务是否存在

查询服务

 >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:\Study\Servers\sctest\sctest.exesctest.exe为上一个章节的服务程序,完全符合windows服务化要求

 >scutil install
 Active code page: 65001
 请输入服务目标程序路径:"E:\Study\Servers\sctest\sctest.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             :

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

 当前时间: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实现脚本化,进而实现程序服务化的交互式操作,



5


总结


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

  • instsrvsrvany

  • Winsw

  • Nssm

获取上述内容中的服务测试源码项目,可关注微信笔者的公众号,回复【sc.bat

往期推荐
点击阅读原文,更精彩~
浏览 81
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报