Pick one... Or read the entire page and learn all the techniques.
Windows Scripting QBASIC SET
Batch Menu CHOICE
FC/DATE COPY CON
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 /p userin=Please enter your full name:
@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 offIn 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.
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%
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 nulNotice 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:
Comparing files CON and nul
this is a test
Resync failed. Files are too different
****** CON
this is a test
****** nul
******
C:\Temp>fc /lb1 /n con nulNow 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.
Comparing files CON and nul
this is a test
Resync failed. Files are too different
****** CON
1: this is a test
****** nul
******
C:\Temp>
C:\Temp>fc con nul /lb1 /n | dateIf 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:
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>
Enter new date (mm-dd-yy): 1: this is a testNow, 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=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.
:loop
set name=%name% %5
shift
if not "%5"=="" goto loop
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.
fc /lb1 con nul | find /v "*****" | find /v "Resync failed." | find /v "Comparing files" > temp.txtBy 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:
copy fragment.txt + temp.txt temp.bat
set name=Foo, King of BarNow you can call temp.bat and you'll have your user input line preserved intact in an environment variable.
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 offAgain, the ansi codes above must be preceeded by an escape character
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%
Lost? Look at the site map.
Bad links? Questions? Send me mail.