Last active
November 24, 2025 11:36
-
-
Save Cube707/62534a33796dd946ea0288b6cf249d2b to your computer and use it in GitHub Desktop.
Windows CMD batch file argumetn parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @echo off | |
| setlocal EnableDelayedExpansion | |
| title %~nx0% %* | |
| rem here is space for global variables, | |
| rem for example, if options require default values, you define them here | |
| set ARGUMENTS= | |
| :argparse_next | |
| rem if the nex argument is empty, we are done | |
| if [%1] == [] goto :parse_arguments | |
| rem handle special option '--', which stops the parser | |
| rem and treads all remaining options as arguments | |
| if %1 == -- ( shift & goto :argparse_stop ) | |
| rem handle both long and short options and potential values | |
| for /f "tokens=*" %%a in ('"echo %1|findstr /r "^[-][a-Z0-9]$ ^[-][-][a-z0-9-]*$""') do ( | |
| rem `call` cant't be used here, as it defines new %n and we need them to get values for options | |
| rem so we use this crude version of call with a jump-label immediately after | |
| goto :parse_options | |
| :option_done | |
| shift & goto :argparse_next | |
| ) | |
| rem handle compact short options, splitting them into all the separate short options | |
| rem for example '-abc' will be split into '-a -b -c' | |
| for /f "tokens=*" %%a in ('"echo %1|findstr /r "^[-][a-Z0-9]*$""') do ( | |
| set tmp=%%a | |
| :multiopt_loop | |
| set tmp=!tmp:~1! | |
| if defined tmp ( | |
| set _ARGPARSE_SUBCALL=1 | |
| call :argparse_next -!tmp:~0,1! || set _ARGPARSE_FAILED=1 | |
| goto :multiopt_loop | |
| ) | |
| set _ARGPARSE_SUBCALL= | |
| shift & goto :argparse_next | |
| ) | |
| rem all remaining args get stored in ARGUMENTS | |
| set "ARGUMENTS=%ARGUMENTS% %1" | |
| shift & goto :argparse_next | |
| :argparse_stop | |
| if not [%1] == [] ( | |
| set "ARGUMENTS=!ARGUMENTS! %1" | |
| shift & goto :argparse_stop | |
| ) | |
| goto :parse_arguments | |
| :argparse_final | |
| rem finalise the parser, handle potential errors | |
| if defined _ARGPARSE_SUBCALL ( exit /B %_ARGPARSE_FAILED% ) | |
| if defined _ARGPARSE_HELP ( call :usage & exit 0 ) | |
| if defined _ARGPARSE_FAILED ( call :usage & exit 1 ) | |
| rem now we can parse the arguments | |
| goto :main | |
| :parse_arguments | |
| rem handle the positions arguments by parsing the first to nth tokens | |
| rem CMD auto-assigns the letters following the one given in the definition. | |
| for /f "tokens=1-2,* delims= " %%A in ("%ARGUMENTS%") do ( | |
| set "ARG_FIRST=%%A" | |
| set "ARG_SECOND=%%B" | |
| rem handle the other arguments, in this case: print them and error | |
| if not [%%C] == [] ( | |
| set _ARGPARSE_FAILED=1 | |
| for %%a in (%%C) do echo:unexpected argument: %%a 1>&2 | |
| ) | |
| rem alternatively, store them in a variable to use later | |
| rem set "ARG_REMAINING=%%C" | |
| ) | |
| goto :argparse_final | |
| :parse_options | |
| if %1 == -a ( | |
| set OPT_A=true | |
| goto :option_done | |
| ) | |
| if %1 == -b ( | |
| set "OPT_B=%2" & shift | |
| goto :option_done | |
| ) | |
| if %1 == -d ( | |
| set BUILD_CONFIG=debug | |
| goto :option_done | |
| ) | |
| if %1 == --debug ( | |
| set BUILD_CONFIG=debug | |
| goto :option_done | |
| ) | |
| if %1 == -h ( | |
| set _ARGPARSE_HELP=true | |
| goto :option_done | |
| ) | |
| if %1 == --help ( | |
| set _ARGPARSE_HELP=true | |
| goto :option_done | |
| ) | |
| echo:unknown option: %1 1>&2 | |
| set _ARGPARSE_FAILED=1 | |
| goto :option_done | |
| :usage | |
| echo:USAGE: | |
| echo: %~nx0 [OPTIONS ..] [--] FOO BAR [ARGUMENTS ..] | |
| echo: | |
| echo:OPTIONS: | |
| echo: -a activates A | |
| echo: -b BUZZ use BUZZ as value for the b option | |
| echo: -d, --debug switch to debug mode | |
| echo: -h, --help display this help and exit | |
| exit /B | |
| :main | |
| rem put your main script-content here. the following is just a demo | |
| echo:OPT_A=%OPT_A% | |
| echo:OPT_B=%OPT_B% | |
| echo:BUILD_CONFIG=%BUILD_CONFIG% | |
| echo:ARGUMENTS=%ARG_FIRST% %ARG_SECOND% | |
| echo:ARBITRARY=%ARG_REMAINING% |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
what the parser supports
-a -bis the same as-ab)(so both
--opt fooor--opt=foowork)--is encounters, allowing you to pass options along to subprocesses without beeing parsedgotchas
values will be consumed first without sanity checks, so
-a -bwill use-bas a value for-aif it expects one, even if-bwould be an valid option.unparsed values will show up as "unexpected argument", even when using
--opt=valuecombining short options disables the value consumption. Its not recommended to allow for values for short options.
Help text is not created automatically, it requires manual updates.
How to use
Add you options to the
:parse_optionsblock and add variables which track your optionsAdd your arguments to the
:parse_argumentsblock. Specify the number of arguments you require by changingtokens=1-nand map the parameters%%A-%%Zto your variables. If you keep the,*at the end oftokens, you will get all remaining arguments into the next letter-parrameter.update the helptext in the
:usageblock to your liking.add your script-code under
:main