In batch script "set /p" is a way to get input from the user, from no characters at all to complete lines into a variable. The problem however comes when you need to restrict the input. Depending on the situation it can be quite difficult to do so. Due to the idiosyncrasies of batch as a language 100% validation takes some care.
I will try to show some of the lessor know problems in validating the input.
The way most people use "set /p" has a flaw, it can cause syntax errors and even allow the user to execute code. This isn't so bad unless the script is run elevated with the script itself locked down with file permissions(security hole).
Take this script:
set /p option=Yes or No? if /i "%option%"=="yes" echo YES!!! if /i "%option%"=="no" echo NO!!!
It seems that the precautionary double quotes have been added, but it can fail if the user enters double quote characters at the prompt, if the user enters something like this(including the double quotes):
"=="" echo HAHA&dir&echo I can execute stuff&pause&if "
Of course it won't always be malicious, but it can be a way for the script to crash with syntax errors.
I will introduce some ways to avoid these problems below, but first I will go through some other useful information.
It is little known that "set /p" will return with the variable unchanged if no text is entered at the prompt before the user presses enter. This quirk means that you can set a default option that only requires the user to press enter. Inversely to avoid incidental defaults it is best to clear a variable before using "set /p" on it.
set option=default set /p option=Just press enter echo %option%
The above should echo "default" if the user doesn't enter any text. The default can be any text you can set to the variable. Using defaults in this way has a desirable side effect, it guarantees that the variable always contains *something* regardless.
To clear a variable before use simply set the variable with no text(including spaces) after the "=".
Because batch has no while construct, labels and goto are needed to re-prompt the user when validation fails. Be sure that every label is unique because multiple labels with the same name in a script causes problems due to ambiguity.
:input set option= set /p option=enter something if not defined option goto input echo you entered some text!
The first check
If you are not setting a default option the first check should always be a check to see if the variable contains something. It won't hurt to do this if you are setting a default, but it won't help either.
:input set option= set /p option=enter something if not defined option goto input
In batch a variable can't actually contain "nothing", an empty variable is an undefined variable. "if not defined" is the best option to test this because 'if "%option%"=="" goto label' can lead to unwanted code execution and "%var:str=rep%"doesn't work on undefined variables.
In fact one of the main reasons to check the variable is defined is so that %var:str=rep% can be used to strip out any double quotes that may hinder the validation.
Now that we are sure that the variable contains something we can check it against predefined options.
:input set option= set /p option=Yes or No if not defined option goto input if /i "%option:"=%"=="Yes" goto yes if /i "%option:"=%"=="no" goto no goto input :Yes echo You Entered Yes! pause goto :eof :no echo You entered no pause goto :eof
There are a few things to note here, firstly notice the "/i" switch to IF, it makes the string comparison case insensitive. Secondly "%option:"=%" the ':"=' removes all of the double quotes from the string(but doesn't change the variable), notice that the variable is still enclosed in double quotes, this is so other "special characters" don't cause other issues.
A point that that is often missed is the "goto" after the last predefined option, it is very important because the input isn't guaranteed to match any option, and failing to address this will cause the flow to "fall through" to whatever code happens to be below, including "end of file" which will exit the script.
Delayed expansion variablesDelayed expansion changes input validation slightly, one step can be omitted. The ':"=' for removing double quotes is unnecessary for variables signeled with exclamation marks("!vars!") and the double quotes are escaped by this type of variable.
Character set Validation
Sometimes it is appropriate to have only certain characters input, such as numbers only. "For /f" can help out with this by using delimiters to exclude wanted characters. If there is any characters that aren't wanted the for loop can sort them out.
:input set option= set /p option=Enter a number: if not defined option goto input for /f "tokens=1* delims=0123456789" %%a in ("A0%option:"=%") do if not "%%b"=="" goto input set option=%option:"=% echo %option% is a number!
You may notice that used two tokens and added a character that does not exist in delim set followed by one that does. This is to get around "eol"(which defaults to ";") issues in the for loop. If the user enters the eol character first the for loop would ignore the line and skip execution. The non-delim followed by delim ensures that the first character passed to the for loop can't be the eol character.
This method can be used with any character that "for /f" can take as a delimiter, which is almost any ASCII or extended ANSI character, except a double quote.