Getting User Input


There are seven main ways of getting user input. Not all work on all systems! Windows Scripting should work on Win98 and newer, and on Win95 if scripting is installed. QBASIC works on everything if it's installed, but nobody ever installs it. SET only works on Win2000 and newer. The Batch Menu works everywhere, but is exceedingly lame. CHOICE works on 9x, but not on NT or newer unless it gets installed. FC/DATE is strictly a Win9x solution. COPY CON is strictly DOS 6 and older because it requires ANSI.

Pick one... Or read the entire page and learn all the techniques.

Windows Scripting   QBASIC   SET   Batch Menu   CHOICE   FC/DATE   COPY CON  


Windows Scripting  If you have Win98, NTSP4, or something newer, you should have Windows Scripting. If you don't have it, download it. Here I show a batch file that runs a pre-built visual basic script "userin.vbs". That script, when run, will create a simple one-line batch file "~userin.bat" which puts the user input into the environment. Because this is a batch web page and not a scripting web page, all you'll get is the sample script with no explanation...

Here is the batch file:

start /w wscript.exe userin.vbs
call ~userin.bat
del ~userin.bat
echo You entered %USERIN%

Now the script file I call "userin.vbs"

strUserIn = InputBox("Enter Data")
Set fs = CreateObject("Scripting.FileSystemObject")
strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
strFileName = fs.GetAbsolutePathName(strFileName)
Set ts = fs.OpenTextFile(strFileName, 2, True)
ts.WriteLine "set userin=" & strUserIn
ts.Close

While the "two separate files" version above will run on any machine that has scripting, trying to write a batch file that will create the script "on the fly" has to be handled differently under NT and 9x. That's because the ampersand has special meaning under NT. When the batch file tries to "echo" a "&" character, NT requires that a caret "^" precede it. If you want to write a batch file that works under NT and 9x, you need to test for how ampersands are handled:

@echo off
:: First test to see if we are on NT or similar OS
:: The ony difference is how they handle the ampersand
 > ~userin.vbs echo 1234&rem
type ~userin.vbs | find "rem" > nul
if errorlevel 1 goto WINNT
goto WIN9X

:WIN9X
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" & strUserIn
>> ~userin.vbs echo ts.Close
goto RUN

:WINNT
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName ^& "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" ^& strUserIn
>> ~userin.vbs echo ts.Close
goto RUN

:RUN
:: Now run the created script
start /w wscript.exe ~userin.vbs
del ~userin.vbs

:: Now call the created batch file
call ~userin.bat
del ~userin.bat

:: Now display the data!
echo You entered %USERIN%
pause
cls

If you find yourself needing to enter passwords and you want to mask the passwords so they aren't visible, you can use a combination of scripting and HTML or (if you have IE5 or newer) you can use scripted HTA files.
 
There is another scripting approach that stays in the DOS box (it doesn't use a graphical window input method). It uses the "Wscript.StdIn" object to get user input. The only bad part is that Wscript.StdIn isn't part of the scripting client that shipped with Win98 or with NTSP4. However, since everybody keeps their computers updated these days(except grandma on dialup), everybody should have a fairly recent version of scripting.

Here's a batch file that lets the user enter a number. It works by having the script exit with an error code that matches the number:

echo wscript.quit wscript.stdin.readline> ~userin.vbs
echo Please enter a number
cscript.exe //NoLogo //B ~userin.vbs
echo You entered %errorlevel%
del ~userin.vbs

Actually, referring to the %errorlevel% variable like I show only works on NT and newer.  Code that works everywhere would test for every separate errorlevel using  the typical "if errorlevel 9 goto label9" syntax. 

If we try to extend the above five line example to allow any text (not just numbers) to be input, it gets a bit more complex. In fact, it ends up looking exactly like the first example(s) except that the line:

 > ~userin.vbs echo strUserIn = InputBox("Enter Data")

gets replaced with this:

 > ~userin.vbs echo strUserIn = wscript.stdin.readline


Right now you're thinking (maybe) that if  "InputBox" and "wscript.stdin.readline" can be swapped so easily, can we use the InputBox in the short numbers-only batch file? Sure!

echo wscript.quit InputBox("Please enter a number")> ~userin.vbs
cscript.exe //NoLogo //B ~userin.vbs
echo You entered %errorlevel%
del ~userin.vbs




QBASIC.  If you aren't using Win9x and want to stick with a non-gui interface, this is it. QBASIC isn't installed on most systems, so you may have to force a download or put the qbasic program files in the same location as your batch file (real easy if it's a network drive!). Again (just like above), I'll teach batch, but not QBASIC. So I'll show a batch file creating a qbasic script, but I won't explain the script. I can only do so much!

The batch file here will generate a separate qbasic script "~usrin.bas", then run that script with qbasic. All the script does is create a separate batch file "~usrin.bat" that puts the user input into the environment.

@echo off
echo OPEN "~usrin.bat" FOR OUTPUT AS #1> ~usrin.bas
echo INPUT "Enter your name ", sUsrin$>> ~usrin.bas
echo PRINT #1, "set usrin="; sUsrin$>> ~usrin.bas
echo CLOSE #1>> ~usrin.bas
echo SYSTEM>> ~usrin.bas
qbasic /run ~usrin.bas
call ~usrin.bat
del ~usrin.bat
del ~usrin.bas
echo Your name is %usrin%
pause
cls

I've had scattered reports that the above code doesn't run on code page 437 (US) but works on code page 850 (Multilingual). But I use code page 437, and the code works identically for me under Win95 and NT4. Just FYI.  Eric Rose, someone who knows NT better than I do, found that if you have problems with QBASIC using expanded memory in ways NT finds unacceptable, you can make everybody happy by adding this line

set RTVMEXP=0

to the beginning of the batch file (actually the second line, just after the "@echo off" line).

In the above example, I create the QBASIC  code as it is needed. You can speed things up considerably by creating the code ahead of time and having it kept as a permanent item. Here is  the code rewritten as two separate files.  First, the batch file:

@echo off
qbasic /run userin.bas
call ~userin.bat
del ~userin.bat
echo Your name is %userin%

Now the QBASIC file I call "userin.bas"

OPEN "~userin.bat" FOR OUTPUT AS #1
INPUT "Enter your name ", sUsrin$
PRINT #1, "set userin="; sUsrin$
CLOSE #1
SYSTEM

If you find yourself needing to enter passwords and you want those passwords masked, you can use QBASIC to mask the passwords with asterisks.



SET.  This only works on Windows 2000 and newer. But it's about time Microsoft built in a way to get user input! When SET is used with the /P option, it will prompt the user for the value. For example:

set /p userin=Please enter your full name:

That will display the prompt "Please enter your full name: " (without the quotes), then assign whatever the user enters to the variable "userin".  If you're sharp, you may notice I have a space at the end of the code line after the colon! That's because the user input occurs immediately after the prompt, so if you don't leave a space, it looks pretty bad.




Batch file menus.  Don't laugh. This is the first thing they teach in most DOS books as how to get user input. If you want the user to choose one of three options, you make a simple menu, then make three batch files whose names correspond to the menu choices:
@echo off
echo Please pick a number and hit Enter:
echo 1 - Doom
echo 2 - Duke
echo 3 - Keen
After the above batch file runs, the user is dropped back to a DOS prompt. All you'd have to do is create three more batch files ( 1.bat, 2.bat, and 3.bat ) which would launch the appropriate programs.


CHOICE.  At least you won't have to press Enter. CHOICE responds immediately to your keypress. Unfortunately, the CHOICE command is not generally available under NT. You can get it on the disk version of the Resource Kit, but not on the download version. If you really want to implement this Win9x solution on NT, you can always steal a copy of  CHOICE.EXE from a Win9x box. All reports I've heard say it works just fine. If a Win98 box isn't handy, download it from:  ftp://ftp.microsoft.com/Services/TechNet/samples/PS/Win98/Reskit/SCRPTING/

@echo off
choice /n /c:123 Please choose 1 for Doom, 2 for Duke, or 3 for Keen:
if errorlevel 3 echo Keen
if not errorlevel 3 if errorlevel 2 echo Duke
if not errorlevel 2 if errorlevel 1 echo Doom
In this case, all I did was echo the word corresponding to the number you select. You'd probably want to run a more useful command or launch another batch file. Read the help on IF to learn more about ERRORLEVEL. Because ERRORLEVEL always tests for a "greater than or equal to" condition, complicated testing for exact error levels like shown above (Or using lots of GOTOs and labels) is usually necessary.

If you want to use CHOICE in an somewhat more complicated way, you could have it check for every possible keypress, then accumulate each key in an environment value. This way you could build up an entire word or number a character at a time. The following example shows a limited password-entry example:

@echo off
set userin=
echo Please enter a sequence of letters a, b, or c. Enter q to quit.
:rerun
choice /n /c:abcq > nul
for %%x in (1,2,3,4) do if errorlevel %%x goto add%%x
:add1
set userin=%userin%a
goto rerun
:add2
set userin=%userin%b
goto rerun
:add3
set userin=%userin%c
goto rerun
:add4
echo Your sequence was %userin%
In the above example, I only checked for three legal input characters, but it is easy (though code-intensive) to extend. Also note how I seem to have suspended the rules for ERRORLEVEL testing on the FOR line. It seems that FOR passes the values on to IF in reverse order. Turning echo on shows FOR evaluates in 1234 order, but IF gets it as 4321. After the first true condition, the GOTO stops IF from coming back to evaluate the next condition.


FC and DATE All the batch gurus know this trick. Just like the "copy con" example above, we use the CON device to get user input. But instead of trying to define the end of con by adding a ctrl-z, we switch viewpoints and define when we will quit reading con. The FC command can do this with it's /LB option. The trick is to use FC not to compare files (which is what FC is made for), but to compare the CON device with the NUL device. Treating devices like files comes easy to Unix types, so it'll make you seem more worldly if you act like it doesn't bother you.

Now, the NUL device has nothing in it, so when FC compares NUL to CON, the difference will always be exactly what the user keys into CON. Duh. But FC's /lb option allows us to specify how many different lines will be accepted. With /lb1 specified, FC will quit reading con after the first different line (which will be the first line). All the user has to do is hit "Enter" to define the end of the line.

We also use FC's /n (line numbering) option for two reasons: First, FC puts out quite a few lines. Having it number the lines makes it easy to FIND the line we want. Second, we'll be putting the output of FC into the input of DATE (wonder why?). By numbering the line, we can allow for the otherwise embarrassing problem of having the user enter as a first word something that might be interpreted as a date. The first word will be "1:", which is not a date.

Let me illustrate FC. First, with no options. Notice how I had to enter a Ctrl-z to terminate things. FC's output starts with the line containing "****** CON". My input is the line "this is a test".

C:\Temp>fc con nul
Comparing files CON and nul
this is a test
^Z
****** CON
this is a test
****** nul
******

Next, I'll add in the /LB1 option.

C:\Temp>fc /lb1 con nul
Comparing files CON and nul
this is a test
Resync failed.  Files are too different
****** CON
this is a test
****** nul
******
Notice all I had to do was hit Enter. Extracting the original line from FC's output poses problems. No matter how you configure FIND, a user could enter something which could mess you up. So yes, now I'll demonstrate FC's line numbering:
C:\Temp>fc /lb1 /n con nul
Comparing files CON and nul
this is a test
Resync failed.  Files are too different
****** CON
     1:  this is a test
****** nul
******



C:\Temp>
Now it would be trivial to use FIND to extract the desired line by searching for "1:". But I want you to notice something else. This time I showed the next prompt. See how much room there is between FC's output and the prompt? FC always adds a blank line to it's output. This is going to come in real handy, because next I'll pipe the output of FC into DATE.
C:\Temp>fc con nul /lb1 /n | date
this is a test
Current date is Wed 05-14-1997
Enter new date (mm-dd-yy): Comparing files CON and nul

Invalid date
Enter new date (mm-dd-yy): Resync failed.  Files are too different

Invalid date
Enter new date (mm-dd-yy): ****** CON

Invalid date
Enter new date (mm-dd-yy):      1:  this is a test

Invalid date
Enter new date (mm-dd-yy): ****** nul

Invalid date
Enter new date (mm-dd-yy): ******

Invalid date
Enter new date (mm-dd-yy):

C:\Temp>
If you've ever tried to set the date, you've noticed how persistent DATE is. It will keep asking you to enter a new date until you either enter a date or until you just press Enter. Since we never (in this example) give DATE a valid date, it will keep rejecting our lines until it hits the blank line at the end of FC's output. If you count, you'll see my single line has resulted in twenty lines of output from DATE (6 of which are blank). By piping DATE's output through FIND looking for "1:", we'll end up with just one line:
Enter new date (mm-dd-yy):      1:  this is a test
Now, if we were to take that line and call it a batch file (Which I'll call TEMP.BAT), when we ran it it would try to execute the ENTER command (Since "Enter" is the first word on the line). Luckily, there is no "enter" command, so we can write our own batch file called ENTER.BAT. When TEMP.BAT runs, it will run our ENTER.BAT and pass new date (mm-dd-yy): 1: this is a test to ENTER.BAT as arguments. Notice how "this" (The first word I typed) is the fifth argument ("new" is first, "date" is second, etc.).

Now it's time to show the completed example. This only asks for one word:

echo Enter your first name
echo set name=%%5>enter.bat
fc con nul /lb1 /n | date | find " 1: " > temp.bat
call temp.bat
del temp.bat>nul
del enter.bat>nul
echo Your name is %name%.

In this example, because my ENTER.BAT was very simple (set name =%5), I created it by using echo in the second line of the example. Your ENTER.BAT can contain anything you want. If you have a need to process an unknown number of words, just keep using SHIFT to get the next argument. Test each argument after you get it to see if it is blank. If it is, you have no more arguments. Here is an example ENTER.BAT illustrating this:

set name=
:loop
set name=%name% %5
shift
if not "%5"=="" goto loop
A word of explanation is in order. The above 5 code lines are junk (even though they work). A space will be inserted in front of each argument as the "name" value is built (See the space between the %name% and the %5). If only one word is entered, it will have a space in front of it. You can (should) change things so the space goes after each word -- and only if there is another word that will follow it. I didn't because you can't see a space at the end of a line (So how can I show you code you can't see?). Additionally, if the user enters any of the many DOS delimiters (space, comma, equal, semicolon...) or multiple delimiters, you will just convert it to a single space. Maybe you want that. Maybe not. Just keep it in mind.

Generally, if you write a batch file that expects multiple arguments to be entered by the user on a single line, you're asking for trouble. Think about how much easier it is to get user input and verify it's validity if you do it one step at a time. Nobody enjoys retyping an entire line because one word got messed up the first time. But you'll assume mistakes will never happen.and write multiple-argument code anyway. You can reference %5, %6, %7 (for example) as your first, second, and third arguments. You can go all the way to %9 for your fifth argument. Usually it's enough. If you expect more than five arguments, you'll need to use shift.


If you absolutely must capture an entire line of user input and you know it will contain punctuation or multiple spaces, it can be done. Above (far, far above) you saw how fc /lb1 con nul returns an unadulterated user line. Separate it out with FIND and save it to a file. Add that file (concatenate it) to a "line fragment" which assigns the entire line to an environment variable. Assume you have pre-made a line fragment which says set name= (and like all fragments, it has no "return" at the end of the line). For this example, I'll assume the name of the fragment is fragment.txt:
fc /lb1 con nul | find /v "*****" | find /v "Resync failed." | find /v "Comparing files" > temp.txt
copy fragment.txt + temp.txt temp.bat
By doing reverse searches for "******", "Comparing files", and "Resync failed.", I hope that only the user line will appear in temp.txt. If the user types "Foo, King of Bar", then temp.bat will contain:
set name=Foo, King of Bar
Now you can call temp.bat and you'll have your user input line preserved intact in an environment variable.


COPY CON  This used to be very popular in the DOS 5 and 6 days, but it requires ANSI.SYS in any reasonable implementation. I'll cover this method for historical interest. Yes, that means you can stop reading unless you support DOS 6 and older machines. CON, the system console (the keyboard and display), can be treated like a file. If you copy a file to con, it appears on the display. If you copy con to a file, it will put whatever you type into the file. The problem is that there is no way to tell how big the "con" file is. Should a command like
copy con testfile.txt
stop after getting one character? A hundred? A thousand? Since con has no length, we have to designate the end of the file by including the almost-obsolete DOS end-of-file character Ctrl-z (decimal 26, hex 1A). When you're done typing, you hold down the "Ctrl" key, tap the "z" key, let off both keys, then hit "Enter". Great for you, but can you imagine trying to explain that to an office full of users? The solution was to redefine the Enter key so that it sends both a Ctrl-z and Enter. If you have device=ansi.sys in your config.sys file, you can do it. The trouble is that you can't depend on anybody else to have ansi in their config.sys file. There is another problem. If you can redefine Enter to send a Ctrl-z, why can't you redefine it to send a "format c:" command? The answer is that you can. This is known as an ANSI BOMB. If you have ansi loaded and you view a file with such a bomb in DOS (by using the TYPE command), you can be screwed. There are several ansi replacements that warn you about or stop such keyboard redefinitions. PKWare includes one if you register PKZIP (Because those message screens that display when you unzip something in DOS can hide bombs). So virtually nobody uses ansi except in terminal emulators (which are immune to bombs, since they aren't at a DOS prompt).

Anyway, if you really want to do it, the code to redefine the Enter key to "Ctrl-z Enter" is [13;26;13p and the code to return Enter to normal is [13;13p. Both these codes must be preceeded by the "escape" character (which doesn't print, so isn't displayed on this web page). An escape character can be generated in Windows Write by holding down the Alt key while typing in 027 using the numeric keypad. You can generate an escape in DOS EDIT by hitting Ctrl-p, then the Esc key. The classic use is to append the con to a line fragment, creating a batch file. Suppose you had a pre-existing line fragment called userfrag.txt containing set userin=

@echo off
echo Enter your name: [13;26;13p
copy userfrag.txt + con temp.bat
echo [13;13p
call temp.bat
del temp.bat
echo Your name is %userin%
Again, the ansi codes above must be preceeded by an escape character
 
 

 


Lost? Look at the site map.

Bad links? Questions? Send me mail.

Google
Yahoo
Ask Jeeves