在日常開發(fā)過程中蹲缠,我們經(jīng)常會(huì)寫一些腳本來幫助我們實(shí)現(xiàn)部分自動(dòng)化的功能。而在使用腳本的過程中刊棕,參數(shù)解析又是經(jīng)常使用的功能察郁。本文將介紹 windows 平臺(tái)上 bat 腳本如何進(jìn)行參數(shù)解析。
1 參數(shù)的組成
首先我們來看一下命令的組成轩缤,一個(gè)完成的命令由命令(command)命迈、選項(xiàng)(option)和位置參數(shù)(position argument)組成。如 git checkout -b dev
命令中火的。git
為主命令(command)壶愤,checkout
為子命令(sub-command),-b
為選項(xiàng)(option)而 dev
為位置參數(shù)(positional argument)馏鹤。
- 主命令和子命令統(tǒng)稱為命令(command)征椒,是必不可少的一部分。有些復(fù)雜的命令會(huì)將命令分為主命令和子命令兩個(gè)部分湃累,如 git 命令勃救、ros 命令、docker 命令等治力。但是對(duì)于大部分簡(jiǎn)單的應(yīng)用一個(gè)主命令就夠了蒙秒。
- 選項(xiàng)(option)是控制命令行為最常用的方式,一般一個(gè)選項(xiàng)都會(huì)有長(zhǎng)選項(xiàng)和短選項(xiàng)兩種方式宵统。如
node -h
和node --help
晕讲。一般長(zhǎng)命令由--
開始而短命令由-
開始。雖然 windows 風(fēng)格的選項(xiàng)是以/
開始(如rd /s /q my_dir
),但是由于 linux 風(fēng)格的選項(xiàng)更加通用瓢省,因此這里將使用 linux 風(fēng)格的選項(xiàng)弄息。有些選項(xiàng)可能需要指定值,選項(xiàng)的賦值方式一般有兩種形式:-
選項(xiàng) 值
勤婚,如mysql --host localhost
. -
選項(xiàng)=值
摹量,如mysql --host=localhost
.
-
- 位置參數(shù)(positional argument)往往是命令中的必選參數(shù)。如 git 中的切換分支命令馒胆,你必須要指定一個(gè)分支荆永,因此這里分支名就是一個(gè)必選參數(shù)。又比如打開文件命令国章,文件是必選項(xiàng),因此文件應(yīng)該是打開文件命令的一個(gè)位置參數(shù)豆村。但是位置參數(shù)也不一定是必選的液兽,在一些命令中,也有可能包含一些可選的位置參數(shù)掌动。一般情況下四啰,位置參數(shù)會(huì)放在命令的最后。由于位置參數(shù)的使用相對(duì)來說沒有那么嚴(yán)格粗恢,因此在一些的命令中柑晒,都沒有位置參數(shù)。比如
ls
命令眷射。位置參數(shù)必須要在前面加上任何前導(dǎo)符匙赞,如curl [options...] <url>
命令中,最后的url
就是位置參數(shù)妖碉。
2 示例說明
介紹完參數(shù)的基本組成之后涌庭,接下來我們就開始設(shè)計(jì) bat 的參數(shù)解析方式了。由于我們使用 bat 腳本往往不會(huì)實(shí)現(xiàn)太復(fù)雜的功能欧宜。因此這里設(shè)計(jì)的參數(shù)解析也有一些簡(jiǎn)化坐榆,這里并不會(huì)使用位置參數(shù),需要位置參數(shù)的地方冗茸,我們可以使用必須選項(xiàng)來代替席镀。這里的參數(shù)解析主要實(shí)現(xiàn)以下功能:
- 選項(xiàng)包括長(zhǎng)選項(xiàng)和短選項(xiàng)兩種方式。
- 選項(xiàng)包括需要賦值的選項(xiàng)和無需賦值的選項(xiàng)夏漱。
- 選項(xiàng)參數(shù)無效的時(shí)候給出錯(cuò)誤警告豪诲。
3 示例代碼
@echo off
set virtual_env=conan
setlocal EnableDelayedExpansion
set is_workon=0
set valid_param=0
set set_vir_env=0
set set_force=0
set set_help=0
set force=0
cd ..
set pro_root=!cd!
:loop
if not "%1"=="" (
set valid_param=0
if "%1" == "--vir_env" set set_vir_env=1
if "%1" == "-v" set set_vir_env=1
if "!set_vir_env!"=="1" (
if "%2"=="" (
echo parameter not enough, virtual environment must be give.
goto :end
)
set virtual_env=%2
set valid_param=1
shift
)
if "%1" == "--force" set set_force=1
if "%1" == "-f" set set_force=1
if "!set_force!"=="1" (
set force=1
set valid_param=1
)
if "%1" == "--help" set set_help=1
if "%1" == "-h" set set_help=1
if "!set_help!" == "1" (
echo USAGE: %0 options.
echo options:
echo `--vir_env/-v value`: set conan virtual environment to the value, default is 'conan'.
echo `--force/-f`: overwrite exist package.
echo `--help/-h`: print this message and exit.
goto :end
)
if "!valid_param!" == "0" (
echo "unknown options: %1, use `-h` to show all avaliable options."
goto :end
)
shift
goto :loop
)
CALL workon !virtual_env!
set is_workon=1
if exist "conan_builds" rd /q /s "conan_builds"
mkdir conan_builds
cd conan_builds
REM build and install
cmake -DCMAKE_INSTALL_PREFIX=%pro_root%/conan_install ..
cmake --build . --config Release
ctest -VV -C Release
if errorlevel 1 (
echo "ctest failed."
goto :end
)
cmake --install . --config Release
REM package
cd !pro_root!/conan_pkg
if "!force!"=="1" (
conan export-pkg . common/dev --package-folder=!pro_root!/conan_install -f
) else (
conan export-pkg . common/dev --package-folder=!pro_root!/conan_install
)
if errorlevel 1 (
echo "package failed."
goto :end
)
REM test
cd !pro_root!/conan_pkg/pkg_test
if exist "builds" rd /q /s "builds"
mkdir builds
cd builds
conan install ..
cmake ..
cmake --build . --config Release
bin\main.exe
if errorlevel 1 (
echo "package test failed."
goto :end
)
:end
if "!is_workon!" == "1" (
CALL deactivate
)
cd !pro_root!/scripts
這個(gè)示例代碼是用來實(shí)現(xiàn) c++ 庫的自動(dòng)編譯、測(cè)試麻蹋、打包跛溉、測(cè)試包的流程。由于這里我們主要是學(xué)習(xí)如何使用 bat 的參數(shù)解析。因此具體的執(zhí)行內(nèi)容其實(shí)我們不用關(guān)心芳室。這里一共使用了三個(gè)參數(shù):
-
-v, --vir_env
用來指定 python 的虛擬環(huán)境名稱专肪。這是一個(gè)帶值的選項(xiàng)。這里注意獲取到值時(shí)堪侯,需要額外執(zhí)行一次shift
嚎尤。 -
-f, --force
如果之前已經(jīng)打包過相同的包了,是否覆蓋之前的包伍宦。這是一個(gè)不帶值的選項(xiàng)芽死。 -
-h, --help
用來顯示幫助信息。
在輸入其他無效的選項(xiàng)時(shí)次洼,會(huì)報(bào)錯(cuò)关贵。這里在實(shí)現(xiàn)的時(shí)候需要注意一下幾點(diǎn):
- 由于 bat 腳本的
if
語句中,無法實(shí)現(xiàn)兩個(gè)條件的“或”運(yùn)算卖毁,所以這里使用兩個(gè)if
語句來分別判斷一個(gè)選項(xiàng)的長(zhǎng)短形式揖曾,同時(shí)還使用了一個(gè)中間變量保存設(shè)置結(jié)果。 - 部分變量的引用使用
!變量!
的形式亥啦,而不是%變量%
的形式炭剪。這是為了保證這些變量能夠延后解析。如果使用%變量%
來引用變量翔脱,這些變量并不會(huì)隨著我們的賦值而改變奴拦。同時(shí)需要注意的是!變量!
的引用形式需要聲明setlocal EnableDelayedExpansion
。 - bat 腳本在執(zhí)行命令的時(shí)候并不會(huì)因?yàn)槊顖?zhí)行出錯(cuò)而停止届吁。因此我們需要通過判斷
errorlevel
這個(gè)變量來判斷之前的一個(gè)命令是否執(zhí)行成功错妖,如果執(zhí)行失敗可以選擇退出腳本。
其他的語句要么比較容易理解疚沐,或者是和業(yè)務(wù)相關(guān)無需關(guān)心站玄,因此這里不再介紹了。
參考資料:
windows batch SET inside IF not working
How do I get the application exit code from a Windows command line?
Windows Bat file optional argument parsing
How to use logical "OR" operator in batch script