Skip to content

Instantly share code, notes, and snippets.

@Cube707
Last active November 24, 2025 11:36
Show Gist options
  • Select an option

  • Save Cube707/62534a33796dd946ea0288b6cf249d2b to your computer and use it in GitHub Desktop.

Select an option

Save Cube707/62534a33796dd946ea0288b6cf249d2b to your computer and use it in GitHub Desktop.
Windows CMD batch file argumetn parser
@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%
@Cube707
Copy link
Author

Cube707 commented Nov 21, 2025

what the parser supports

  • long (double dashed) and short (single dash, single letter) options
  • positional arguments
  • mixing of options and arguments, only the order of arguments to each other matters
  • single options can be combined (-a -b is the same as -ab)
  • values for both short and long options, which can be separated form the option by either a space or equal-sign
    (so both --opt foo or --opt=foo work)
  • the parser stopes when -- is encounters, allowing you to pass options along to subprocesses without beeing parsed

gotchas

values will be consumed first without sanity checks, so -a -b will use -b as a value for -a if it expects one, even if -b would be an valid option.

unparsed values will show up as "unexpected argument", even when using --opt=value

combining 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_options block and add variables which track your options

Add your arguments to the :parse_arguments block. Specify the number of arguments you require by changing tokens=1-n and map the parameters %%A - %%Z to your variables. If you keep the ,* at the end of tokens, you will get all remaining arguments into the next letter-parrameter.

update the helptext in the :usage block to your liking.

add your script-code under :main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment