bat 脚本参数解析

在日常开发过程中,我们经常会写一些脚本来帮助我们实现部分自动化的功能。而在使用脚本的过程中,参数解析又是经常使用的功能。本文将介绍 windows 平台上 bat 脚本如何进行参数解析。

1 参数的组成

首先我们来看一下命令的组成,一个完成的命令由命令(command)、选项(option)和位置参数(position argument)组成。如 git checkout -b dev 命令中。git 为主命令(command),checkout 为子命令(sub-command),-b 为选项(option)而 dev 为位置参数(positional argument)。

  • 主命令和子命令统称为命令(command),是必不可少的一部分。有些复杂的命令会将命令分为主命令和子命令两个部分,如 git 命令、ros 命令、docker 命令等。但是对于大部分简单的应用一个主命令就够了。
  • 选项(option)是控制命令行为最常用的方式,一般一个选项都会有长选项和短选项两种方式。如 node -hnode --help。一般长命令由 -- 开始而短命令由 - 开始。虽然 windows 风格的选项是以 / 开始(如 rd /s /q my_dir),但是由于 linux 风格的选项更加通用,因此这里将使用 linux 风格的选项。有些选项可能需要指定值,选项的赋值方式一般有两种形式:
    • 选项 值,如 mysql --host localhost.
    • 选项=值,如 mysql --host=localhost.
  • 位置参数(positional argument)往往是命令中的必选参数。如 git 中的切换分支命令,你必须要指定一个分支,因此这里分支名就是一个必选参数。又比如打开文件命令,文件是必选项,因此文件应该是打开文件命令的一个位置参数。但是位置参数也不一定是必选的,在一些命令中,也有可能包含一些可选的位置参数。一般情况下,位置参数会放在命令的最后。由于位置参数的使用相对来说没有那么严格,因此在一些的命令中,都没有位置参数。比如 ls 命令。位置参数必须要在前面加上任何前导符,如 curl [options...] <url> 命令中,最后的 url 就是位置参数。

2 示例说明

介绍完参数的基本组成之后,接下来我们就开始设计 bat 的参数解析方式了。由于我们使用 bat 脚本往往不会实现太复杂的功能。因此这里设计的参数解析也有一些简化,这里并不会使用位置参数,需要位置参数的地方,我们可以使用必须选项来代替。这里的参数解析主要实现以下功能:

  • 选项包括长选项和短选项两种方式。
  • 选项包括需要赋值的选项和无需赋值的选项。
  • 选项参数无效的时候给出错误警告。

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

这个示例代码是用来实现 c++ 库的自动编译、测试、打包、测试包的流程。由于这里我们主要是学习如何使用 bat 的参数解析。因此具体的执行内容其实我们不用关心。这里一共使用了三个参数:

  • -v, --vir_env 用来指定 python 的虚拟环境名称。这是一个带值的选项。这里注意获取到值时,需要额外执行一次 shift
  • -f, --force 如果之前已经打包过相同的包了,是否覆盖之前的包。这是一个不带值的选项。
  • -h, --help 用来显示帮助信息。

在输入其他无效的选项时,会报错。这里在实现的时候需要注意一下几点:

  • 由于 bat 脚本的 if 语句中,无法实现两个条件的“或”运算,所以这里使用两个 if 语句来分别判断一个选项的长短形式,同时还使用了一个中间变量保存设置结果。
  • 部分变量的引用使用 !变量! 的形式,而不是 %变量% 的形式。这是为了保证这些变量能够延后解析。如果使用 %变量% 来引用变量,这些变量并不会随着我们的赋值而改变。同时需要注意的是 !变量! 的引用形式需要声明 setlocal EnableDelayedExpansion
  • bat 脚本在执行命令的时候并不会因为命令执行出错而停止。因此我们需要通过判断 errorlevel 这个变量来判断之前的一个命令是否执行成功,如果执行失败可以选择退出脚本。

其他的语句要么比较容易理解,或者是和业务相关无需关心,因此这里不再介绍了。

参考资料:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342