windowsbatch-filecmdparameters

Processing parameters in my batch project


Alright, I've been working on a project for the past week, it works great and all that, but i added some parameters and commands so more advanced users can manipulate the behavior of my program.

It works fine with the parameters, but I have an issue with running the script normally with no parameters, I tried:

if not defined "%~1" goto load
if not defined "%~2" goto load
if not defined "%~3" goto load
if not defined "%~4" goto load
if not defined "%~5" goto load

But this only works if the script is executed from cmd, so If I run the script normally, it crashes.

Normally as in double clicking it on the desktop, and from cmd as in typing the executable name from there to run it

If I remove the quotes:

if not defined %~1 goto load
if not defined %~2 goto load
if not defined %~3 goto load
if not defined %~4 goto load
if not defined %~5 goto load

Due to %~1, %~2, etc being empty, it processes goto as a string, so I get this:

'load' is not recognized as an internal or external command, operable program or batch file.

I tried removing the tildes:

if not defined "%1" goto load
if not defined "%2" goto load
if not defined "%3" goto load
if not defined "%4" goto load
if not defined "%5" goto load

Quotes and no quotes, it has the exact same behavior as using tildes.

Here's the full parameter processing for additional code if needed:

@echo off
setlocal

set costumver=false
set chosenlabel=vercheck
set nobitcheck=false
set enableall=false
if not defined "%~1" goto load
if not defined "%~2" goto load
if not defined "%~3" goto load
if not defined "%~4" goto load
if not defined "%~5" goto load

set param=0
goto parametercheck

:parametercheck
cls
echo Processing parameters...

set/a param+=1

if "%~%param%" equ 6 goto load
if "%~%param%"=="customver" set costumver=true && goto choosever
if "%~%param%"=="help" goto help
if "%~%param%"=="nopayloadcheck" set chosenlabel=init && goto load
if "%~%param%"=="enableall" set enableall=true && goto loa
if "%~%param%"=="nobitcheck" set nobitcheck=true && goto load
goto parametercheck

:personalization
cls
echo Processing personalization parameters if avalabile....

if not "%~1"=="" set cols=%~1
if not "%~2"=="" set lines=%~2
if not "%~3"=="" set colorcode1=%~3
goto load

This isn't the full script, as I don't want to open source my project yet, but this is everything related to processing parameters in my script.

I'd really like to get this issue fixed, it's been getting on my nerves and I can't find a solution, thanks!

I'm not the best at codeblock-ing text in stack overflow, the code is formatted in a wonky way, sorry about that.


Solution

  • 1. Complete definition of execution environment

    Please read the issue chapters in this answer. It is advisable to define the required execution environment completely in a batch file and do not depend on settings defined outside of the batch file. That can be done in this case by changing the second line from just setlocal to setlocal EnableExtensions DisableDelayedExpansion.

    The first two lines should be:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    

    The execution environment is defined now completely by the batch file itself. It does not longer matter what are the Windows defaults or with which options cmd.exe was started for processing the batch file or how a parent batch file defines the execution environment before calling this batch file.

    2. Best syntax for environment variable definition

    There should be read also: Why is no string output with 'echo %var%' after using 'set var = text' on command line? It is correct to define multiple environment variables without " left to the variable name and one more " after the string value assigned to the environment variable if the string values are fixed and there is made sure on writing the batch file that the lines with set do not end with a trailing space or horizontal tab. The lines 4 to 7 are okay for that reason. But the usage of surrounding " is advisable in almost all use cases.

    Correct is:

    set costumver=false
    set chosenlabel=vercheck
    set nobitcheck=false
    set enableall=false
    

    Better is:

    set "costumver=false"
    set "chosenlabel=vercheck"
    set "nobitcheck=false"
    set "enableall=false"
    

    Command extensions must be enabled for using this better syntax.
    That is made already sure with the second command line in the batch file.

    3. Correct checking of passed argument strings

    The help output on running if /? in a command prompt window explains that if not defined is for checking if an environment variable specified next is not defined in the current list of environment variables.

    Environment variable names are usually not defined with surrounding " although it is possible like with

    set ^""Unusual Name"=Hello World!^"
    

    That defines an environment variable with name "Unusual Name" with the string value Hello World! as it can be seen with a command line like:

    if defined "Unusual Name" echo Variable "Unusual Name" exists with string value: %"Unusual Name"%
    

    But environment variable names are usually without surrounding " and also without a space character which means that this IF condition cannot be used for environment variables with a name requiring surrounding " like a variable name with a space.

    if not defined "%~1" checks for that reason if there is not defined an environment variable with name as defined by the first argument string passed to the batch file with " at the beginning and the end of the environment variable name. That is not what should be checked here.

    The correct syntax is a simple string comparison:

    if "%~1" == "" goto load
    

    The command line results in removing from first argument string passed to the batch file the surrounding " if the first argument string is passed at all to the batch file with surrounding " and next is compared this string with the two " specified in the batch file with the string "" case-sensitive for equality. Please read: Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files. It explains in full details how a string comparison is done on using the string comparison operator ==.

    What does this condition do?

    If batch processing is started without any argument string, the command line becomes:

    if "" == "" goto load
    

    The condition evaluates to true because of the string "" left to operator == is equal to string "" right to the string comparison operator. goto load is executed next for that reason.

    The same happens on starting the batch file with "" as first argument string. Yes, that is valid. The first passed argument string is in this case simply an empty argument string. It depends on what the batch file requires as first argument string if an empty argument string is acceptable or is not allowed at all. Most batch file do not allow an empty argument string.

    Starting the batch file with 6 or "09" or "Development & Test 100% (!)" results in:

    if "6" == "" goto load
    if "09" == "" goto load
    if "Development & Test 100% (!)" == "" goto load
    

    The condition evaluates in all three use cases to false and therefore goto load is not executed by the Windows Command Processor.

    Tricky to handle is a syntax error made by a user or a calling batch file or process on which the last argument string is passed with missing " at the beginning but has " at the end, i.e. the batch file execution is started with the argument string SyntaxError!". That results in:

    if "SyntaxError!"" == "" goto load
    

    The double quote character at the end is not removed by cmd.exe because of there is no double quote character at the beginning.

    That command line results next in the output of the error message:

    The syntax of the command is incorrect.

    The Windows Command Processor exits the processing of the batch file after the output of this error message as it can be seen on debugging the batch file. Most batch files do not handle such a not valid passed argument string because of missing " at the beginning of the argument string.

    For more details about argument string checking and processing see:

    1. error 1"" was unexpected at this time. when first command line argument is double quoted
    2. What is the difference between "..." and x"..." in an IF condition in a Windows batch file?

    4. Environment variable expansion inside argument reference

    There is the command line:

    if "%~%param%" equ 6 goto load
    

    There is expected that cmd.exe parses the command line first to:

    if "%~1" equ 6 goto load
    

    Then the command line should be parsed next with 5 or "5" passed as first argument string to the batch file to:

    if "5" equ 6 goto load
    

    The two compared strings would be never equal as explained in full details by Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files and Dash it! A batch string-comparison conundrum.

    But that does not happen at all. How should cmd.exe know that the first % should be ignored on first parsing of the command line and interpret therefore first just %param% as environment variable reference to replace by the string 1 assigned currently to the environment variable with name param? The Windows Command Processor parses a command line from left to right and interprets %~ as batch file argument reference (or for loop variable reference) and the next % as an invalid argument number. The result is the output of the error message:

    The following usage of the path operator in batch-parameter
    substitution is invalid: %~%param%" equ 6 goto load


    For valid formats type CALL /? or FOR /?
    The syntax of the command is incorrect.

    The Windows Command Processor exits the processing of the batch file after the output of the error message.

    5. Solution for processing the arguments

    This batch file processes all passed arguments referenced with %* as explained by the help output on running call /? in a command prompt window using a FOR loop with several IF conditions.

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    for /F "delims==" %%I in ('set # 2^>nul') do set "%%I="
    
    set "#customver=false"
    set "#chosenlabel=vercheck"
    set "#nobitcheck=false"
    set "#enableall=false"
    set "#cols="
    set "#lines="
    set "#colorcode1="
    
    for %%I in (%*) do (
        if /I "%%~I" == "customver" (
            set "#customver=true"
        ) else if /I "%%~I" == "nopayloadcheck" (
            set "#chosenlabel=init"
        ) else if /I "%%~I" == "enableall" (
            set "#enableall=true"
        ) else if /I "%%~I" == "nobitcheck" (
            set " #nobitcheck=true"
        ) else if not "%%~I" == "" (
            set "InvalidArg="
            for /F delims^=0123456789^ eol^= %%J in ("%%~I") do set "InvalidArg=1"
            if not defined InvalidArg (
                if not defined #cols (
                 set "#cols=%%~I"
             ) else if not defined #lines (
                    set "#lines=%%~I"
                ) else if not defined #colorcode1 (
                    set "#colorcode1=%%~I"
                )
            )
        )
    )
    
    set #
    endlocal
    

    The output on running the batch file with no arguments is:

    #chosenlabel=vercheck
    #customver=false
    #enableall=false
    #nobitcheck=false
    

    The output on running the batch file with the arguments

    "NoPayloadCheck" hello ";34" NOBITCHECK ";Development & Test 100% (!)" 120 enableall 43 -53 "  88" "customver" 09
    

    is:

    #chosenlabel=init
    #colorcode1=09
    #cols=120
    #customver=true
    #enableall=true
    #lines=43
    #nobitcheck=true
    

    It can be seen on this test example that the user of the batch file has the freedom to specify the options

    1. in any order and
    2. in any case and
    3. without or with surrounding " and
    4. even with invalid arguments which are ignored on processing.

    Note: Please remove the third line and the last but one line. Then execute a find for # and replace all found occurrences with an empty string for the removal of all # in the batch file code above. # is used at the beginning of each variable name to be able to use just set # to output all the environment variables defined in this batch file depending on the passed arguments.

    The existing code below no longer needed label load must be inserted above the last command line with the command endlocal which could be even omitted as executed implicit by cmd before exiting the processing of the batch file for the initial setlocal in the second line.

    Please see also: Safe number comparison in Windows batch file

    To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.