Batch file menus.
Don't laugh. 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.
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.
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
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 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= :loop set name=%name% %5 shift if not "%5"=="" goto loopA 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.
fc /lb1 con nul | find /v "*****" | find /v "Resync failed." | find /v "Comparing files" > temp.txt copy fragment.txt + temp.txt temp.batBy 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 BarNow you can call temp.bat and you'll have your user input line preserved intact in an environment variable.
Bad links? Questions? Send me mail.