前言
接過上一章節,sc.exe
進行Windows
中,進行系統程序服務化的操作方式不難發現,直接進行基礎工具服務化操作,一個是人狠話不多,出錯不好說
,一個就是不太優雅,每回都需要自己手動去輸入配置信息,例如:sc [Service name]
程序路徑,服務方式等內容,啥也別說,改成腳本引導,交互式輸入,以下是腳本的幾個需求:
-
啟動腳本出現輸入引導信息
-
輸入相關服務配置
-
完成對目標程序服務操作(注冊、啟動、停止和刪除)
准備作業
腳本是啥?在系統中又叫批處理腳本,用於實現系統使用過程中,通過系統Dos
指令實現部分功能需求的可執行文件,相關交互指令被稱之為批處理腳本,此處簡稱腳本,在Windows
系統中,腳本文件常見以.bat
和.cmd
作為文件后綴
要寫當然啥不會肯定不行,先簡單學一學指令,筆者整理了相關的基本語法,如下:
變量
變量的主要目的是為了接受在批處理文件外部接受輸入值和處理文件內部對變量的設置和使用
系統變量
%CD% 獲取當前路徑
%PATH% 獲取命令搜索路徑/全局環境變量中的PATH
%DATE% 獲取當前日期
%TIME% 獲取當前時間
%ERRORLEVEL% 獲取上一個命令的執行結果碼
輸出內容echo
>echo %DATE%
2021/07/16 周五
設置變量(局部)set
set 變量名稱=變量值
>set test=12
>echo %test%
12
基本指令
以下介紹僅僅包含當前需要實現的批處理文件需要用到的指令,並不是完整指令細化,具體參考可以閱讀對應的微軟文檔參考鏈接
注釋
::注釋內容
輸入
參考鏈接: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.bat
,CHCP 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.bat
或name.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
就開始執行安裝服務的安裝引導,其他操作同理,由於腳本是由多個批處理文件組成,所有批處理結構如下:
判定服務是否存在
查詢服務
>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.exe
,sctest.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
實現腳本化,進而實現程序服務化的交互式操作
總結
這些腳本雖然看似雞肋,只是將指令進行了二次封裝,實際上,這是筆者第一個比較系統化的對腳本的實踐和應用,能夠將這些指令以類似簡單編程的方式進行處理也是一種經驗的積累,后續將繼續講解筆者接觸到的Windows
服務化的其他工具的使用和思考
instsrv
與srvany
Winsw
Nssm
獲取上述內容中的服務測試源碼項目,可關注私信或直接評論回復【sc.bat
】