Batch files: ask for user input with "suggestions" - batch-file

How can I prompt users for an answer, suggesting something they can edit?
I know how to make a q&a like:
1. The sky is:
The user put the answer and:
echo For you the sky is %var%
But i want something like:
1. The sky is: Blue
echo For you the sky is Blue
But the user can change it
1. The sky is: Green
echo For you the sky is Green
I don't know if i was clear, tell me if not.
Thanks

Initialize the variable before with a color:
#echo off&setlocal
set "color=Blue"
set/p "color=For you the sky is %color% (hit enter for o.k. or type in other color): "
echo For you the sky is %color%

You can use WScript.Shell's SendKeys method through JScript or VBScript to simulate keypresses. Give this a shot. I think it does what you're looking for. It will send uneditable 1. The sky is: then simulate the user keystrokes of Blue. The user is then free either just to hit Enter, or he can Backspace to erase "Blue" and replace it whatever he wishes.
Save this with a .bat extension and run it.
#if (#a==#b) #end /*
:: batch portion
#echo off
setlocal
set /p "=1. The sky is: "<NUL
call :sendkeys Blue
set /p "sky="
echo For you the sky is %sky%. Press any key to exit.
pause >NUL
exit /b
:sendkeys <string>
cscript /nologo /e:jscript "%~f0" "%~1"
goto :EOF
:: JScript portion */
var sh = new ActiveXObject("WScript.Shell");
sh.SendKeys(WSH.Arguments(0));

set x=blue
set y=
set /p "y=The sky is [%x%] "
if not defined y set y=%x%
echo For your the sky is %y%
If user input is empty, the predefined %x% is copied to the outputvariable %y%

As other answers said, you must need a third party utility to solve this problem. For example, I used my GetKey.exe and Show.exe auxiliary programs to write ReadLine.bat, a subroutine that simulate SET /P command, that is, it read characters until Enter key is pressed and delete the last character with BackSpace key. We may modify such routine in order to provide an initial value for the input variable:
#echo off
rem Read a variable with an initialized value
rem Antonio Perez Ayala
set Bell=7
set BackSpace=8
set Enter=13
set Space=32
:ReadInitVar var="prompt" ["initial value"]
rem %1 %2 %3
setlocal EnableDelayedExpansion
Show %2
set len=0
if "%~3" equ "" (
set %1=
) else (
Show %3
set "%1=%~3"
for /L %%i in (0,1,80) do if "!%1:~%%i,1!" neq "" set /A len+=1
)
:nextKey
GetKey
set key=%errorlevel%
if %key% geq %Space% (
rem Ascii character: insert it
Show %key%
for /F "delims=" %%a in ('Show %key%') do set "%1=!%1!%%a"
set /A len+=1
) else if %key% equ %BackSpace% (
rem Backspace: delete last character
if defined %1 (
Show %BackSpace% %Space% %BackSpace%
set "%1=!%1:~0,-1!"
set /A len-=1
) else (
rem Empty value
Show %Bell%
)
)
if %key% neq %Enter% goto nextKey
echo/
for /F "delims=" %%a in ("!%1!") do endlocal & set %1=%%a
exit /B
This way, you may use this line to solve your problem:
call :ReadInitVar color="For you the sky is " "Blue"
You may download Getkey.exe and Show.exe auxiliary programs, and review the original ReadLine.bat subroutine, from this site: Advanced Batch features via auxiliary .exe programs.
There is also another subroutine similar to ReadInitVar.bat above, but that display the user input in a different color field by using ColorShow.exe auxiliary program instead of Show.exe. You may review it here: http://www.dostips.com/forum/viewtopic.php?f=3&t=4198&p=23426#p23426

Related

Batch if statements don't seem to work

I am so confused and all help is appreciated...
so I'm making a little game and this is the code:
#echo off
:menu
echo Welcome To My Game!!!
echo Play & echo.Exit
:: add more menu items above
set /p menInp=
if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT
:initFile
cls
if NOT exist \MyAdventureGame\AdventureGameSave.txt\ GOTO newGame
if exist \MyAdventureGame\AdventureGameSave.txt\ GOTO Load
:newGame
echo. 2>AdventureGameSave.txt
set Gold = 50
set Xp = 0
set Level = 1
GOTO Save
:Save
echo %Gold% >>AdventureGameSave.txt
echo %Xp% >>AdventureGameSave.txt
echo %Level% >>AdventureGameSave.txt
GOTO Town
:Load
:: add Load functionallity here
GOTO Town
:Town
echo Welcome to town!!!
PAUSE
Now for my question...
when i run it, no matter what I type for my menInp, my program goes to the next line, :initFile, and its almost like my if statements
(if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT)
are being ignored.
if /i %menInp$==exit EXIT
should be
if /i %menInp%==exit EXIT
or preferably
if /i "%menInp%"=="exit" EXIT
since you are unable to predict the user's input and it may contains spaces. The "s make the string a single token.
This is somewhat of a comment, but may forestall further problems.
Tip for game-generation:
If you reserve a character as a prefix for variables-you-want-to-save (eg all variables I want to save/reload start with #) then all you need to save a game is
set #>"mygamefile.txt"
and all you need to reload a game is
for /f "usebackqdelims=" %%a in ("mygamefile.txt") do set "%%a"
To zap all # variables (useful before reloading a game) use
for /f "delims==" %%a in ('set # 2^>nul') do set "%%a="
Oh and you may consider removing the terminal \ from the filename in the if exist statements (also enclose the entire filename in quotes to allow the name to contain spaces (and follow good practice regardless of whether you actually need to cater for spaces in filenames))

Writing to different lines with Batch

I'm trying to make a reminder system within batch in which there are different lines of reminders. My batch program will write to different lines in a .txt file, but it isn't working. Could you please help and try to find the issues?
#echo off
echo Enter slot # for reminder
set /p n=
cls
echo Please type in the assignment name
set /p a=
echo ----------------------------------
echo Please type in the class
set /p c=
echo ----------------------------------
echo Please type in the date due
set /p d=
cls
if %n%==1 goto l1
if %n%==2 goto l2
if %n%==3 goto l3
if %n%==4 goto l4
if %n%==5 goto l5
if %n%==6 goto l6
:l1
echo Reminder for %c% Homework! %a%,%d% > Reminder.txt
:end
:l2
echo Reminder for %c% Homework! %a%,%d% >> Reminder.txt
:end
:l3
echo Reminder for %c% Homework! %a%,%d% >>> Reminder.txt
:end
:l4
echo Reminder for %c% Homework! %a%,%d% >>>> Reminder.txt
:end
:l5
echo Reminder for %c% Homework! %a%,%d% >>>>> Reminder.txt
:end
:l6
echo Reminder for %c% Homework! %a%,%d% >>>>>> Reminder.txt
:end
Hints to fix what you've got:
The > character won't let you write to specific lines, and there's no native support in Windows batch to do such a thing.
There are two operators that use the > character: >, which redirects output to a file (replacing any existing content), and >>, which appends (adds to the end of) a file.
You've got multiple instances of :end, but that's invalid. :end is a label, which is a unique reference to that point in the code. When you add more than one, some get ignored and you get undefined behaviors, which is bad.
It looks like you're trying to use :end to exit. Use goto :EOF for that. It jumps to the built-in label :EOF, short for End Of File.
You need to handle the case where n is none of the predefined values. Currently if someone entered 7 for n, your program would get to the logic after :l1 and run it, which is wrong. Put a goto :EOF there just in case.
How to approach solving this type of issue with batch:
The only way I can think of off the top of my head to modify a specific line is to iterate through all lines using a for /f loop, rewriting each line (to a temporary file) until you encounter the one you want to change, then write your new content instead of the existing content. Then when you're done iterating, you can replace the original file with that temporary file.
You would have to do this each time you wanted to change a new line. Batch is a really simple language that doesn't have useful constructs like arrays, or the many external tools that a shell scripting language like Bash would have. It's also got some really unsophisticated runtime evaluation.
Here's a partial solution that you can combine with a few lines from your code above to achieve what you want. It prompts you for a line number, then puts the content of the newContent variable (replace with your implementation) into the file at the specified line:
REM suppresses the echo of the commands in the program
#ECHO OFF
REM sets a feature that overcomes some of the weak runtime evaluation limitations that batch has
setlocal ENABLEDELAYEDEXPANSION
REM The name of your file
set fname=file.txt
REM If our file doesn't already exist, make a new one with 6 empty lines since that's all we want for now.
if EXIST "%fname%" goto alreadyExists
for /l %%b in (1,1,6) do echo.>>"%fname%"
:alreadyExists
REM The name of a temp file
set tfile=f2.txt
REM A counter to track the line number
set counter=0
REM Input to get the line number you wish to replace
set /p replacementLine=Type the line number that should be replaced:
REM The content that goes on the replaced line
set newContent=New entry
REM Read the file, iterate through all lines.
for /f "tokens=*" %%a in (file.txt) do (
REM Add one to the counter
set /a counter=!counter!+1
REM Use the redirect '>' operator for the first line
if "!counter!"=="1" (
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >f2.txt
)
) else (
REM else for lines other than the first, use the append redirect '>>'
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >>f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >>f2.txt
)
)
)
REM Delete the original file
del "%fname%"
REM Replace it with the modified copy
ren "%tfile%" "%fname%"
You can replace a few lines at the top get the functionality you want.
you can't write to a specific line in a file with batch. Instead you have to rewrite the complete file.
Steps: a) read the file. b) change the desired line. c) (over)write the file with the new data.
#echo off
setlocal enabledelayedexpansion
if not exist reminder.txt call :InitFile :: create the file, if it doesn't exist
set /p "n=Enter slot # for reminder: "
set /p "a=type in the assignment name: "
set /p "c=type in the class: "
set /p "d=type in the date due: "
cls
call :ReadFile
set "_Line[%n%]=Reminder for %c% Homework: %a%,%d%"
call :WriteFile
type Reminder.txt
goto :eof
:ReadFile
set x=0
for /f "delims=" %%i in (reminder.txt) do (
set /a x+=1
set "_Line[!x!]=%%i"
)
goto :eof
:WriteFile
set x=0
(for /f "tokens=2 delims==" %%i in ('set _Line[') do echo %%i)>Reminder.txt
goto :eof
:InitFile
(for /l %%i in (1,1,6) do echo Reminder %%i [empty])>Reminder.txt
goto :eof
(Note: this would make trouble with more than 9 lines because of the alphabetical sorting with set _line[, but as you need only 6 lines, this should not be a problem for you)
Note: your input shouldn't contain !

(Windows Batch File) IF statement inside FOR LOOP

I'm trying to write manual document validation part of my program. It's basically opening all the pdf documents one by one in the same folder. When its open i would like to echo few possibilities for user. Here starts the problem. I have around 180 possible choices. I was thinking to ask for the first letter of choice. Then it will echo all choices with started with X letter and user has to simply enter the number of this choice. So for example we have :
1. Asomething
2. Asomename
3. Asomenametoo
4. Bname
5. Bname 2
6. Bname 3
I want user to choose first letter and print possible choices. When the choice is made program should add some string to txt file with the same name in the same folder. Here i have a problem with IF statement inside FOR loop. I wanted to use goto but i can't do it inside FOR loop.
I can set up all the strings for each number before. For example : When you choose 1 it will add SomeString to txt. It's important to use choice option to avoid any typo's. Does anybody knows any other way to do this inside FOR loop ?
CODE:
setlocal enabledelayedexpansion
FOR %%b IN (c:\test\*.txt) DO (
IF "%ERRORLEVEL%"=="0" ECHO Document will open now...
start Acrobat.exe %%b.pdf
ECHO 1. Sample 1
ECHO 2. Sample 2
set /p choice= Please enter number:
call :OPTION
ECHO !choice! >> %%b
PAUSE
taskkill /IM Acrobat.exe >> c:\test\log\temp.txt
)
PAUSE
GOTO MENU
:OPTION
IF !choice!==1 SET /A !choice!==MNV666
IF !choice!==2 SET /A !choice!==MNV777
GOTO:EOF
I'm having some trouble understanding the problem you're having, but it looks like all of the statements following the IF should all be conditions of the IF, not just the ECHO statement. For that, you can put the entire block in parentheses like this:
setlocal enabledelayedexpansion
FOR %%b IN (c:\test\*.txt) DO (
IF "%ERRORLEVEL%"=="0" (
ECHO Document will open now...
start Acrobat.exe %%b.pdf
ECHO 1. Sample 1
ECHO 2. Sample 2
set /p choice= Please enter number:
call :OPTION
ECHO !choice! >> %%b
PAUSE
taskkill /IM Acrobat.exe >> c:\test\log\temp.txt
) else (
goto :EOF REM Just an example of else
)
)
PAUSE
GOTO MENU
:OPTION
IF !choice!==1 SET /A !choice!==MNV666
IF !choice!==2 SET /A !choice!==MNV777
GOTO:EOF
Were you having some problem using goto in the FOR loop?

Batch Making Passwords

I have made a batch game where users can log in / register. But there is no point in having passwords if a person standing nearby can peep at the password. Normal password fields mask the characters with asterisks (*).
How can mask characters on a batch file?
I've seen this done on cmd before but I have no clue how.
You can use XCOPY for a hidden input, it can handle nearly all characters and you can also implement a backspace logic.
#echo off
setlocal EnableDelayedExpansion
call :input
echo(
echo '!input!'
if "!input!"=="password" echo OK
exit /b
:input
for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do (
set "\b=%%a"
)
set "input="
:keyLoop
call :GetKey
if not defined key exit /b
if "!key!"=="!\b!" (
if defined input (
set "input=!input:~0,-1!"
<nul set /p ".=!\b! !\b!"
)
) ELSE (
<nul set /p ".=*"
set "input=!input!!key!"
)
goto :keyLoop
:GetKey
setlocal DisableDelayedExpansion
set "key="
for /F "usebackq delims=" %%L in (`xcopy /L /w "%~f0" "%~f0" 2^>NUL`) do (
if not defined key set "key=%%L"
)
(
endlocal
set "key=^%key:~-1%" !
exit /b
)
This code should be able to handle all characters, like ^!&%<>.
It's also possible to use backspace to delete the last entered character.
The line set "key=^%key:~-1%" ! seems odd, but it's used to escape the ! character with set "key=^!" ! in the delayed expansion context.
And to avoid problems for all other characters the last ! removes the caret, like in set "key=^A" ! will be evaluated to ``set "key=A"`
Ok, this is a bit different to what you may have had in mind, but that's you're fault for choosing batch for game dev.
The way I see it is you have 3 options:
Use an external program you self made in C#, C++, Python, [etc.]
Howver this requires an application to already do this for you (Which there probably is) or for you to have a knowledge in one of these languages
Use the choice command, to continuously take one key input and wait for the user to hit space to signify the end of the password
However this limits the password characters choice, and makes the program look ugly
Use 2 Batch threads, one that masks and tallies input while the other stores it to a variable.
This may be a bit dodgey at times, at would be a bit complicated but may be the only choice you have.
Now, as I was typing this an idea stuck my head on how to achieve this. Since it might take some time to test I thought I'd post the idea (as it seems to be a soloution to this problem, which has been around for a while).
Logic
One Batch Thread will simply use set /p to store all the input into a variable and upon completion will communicate to the other batch thread through the use of waitfor or a simple directory file.
Another Batch Thread would loop the pause >nul statement and would tally the number of times the pause statement is looped, printing out the appropriate amount of *'s. The other important job of this thread is to sense when the user has finished typing the password, upon which it exits.
Im starting to make this batch program now, but for now I'll just keep you informed of my idea so far.
Code
Login.bat
#echo off
Title Password Please:
:: This is the main code
REM This code relies on Masker.bat
REM SET password to be first and thrid letter,
REM of the day of the week.
set pass=%Date:~0,1%%Date:~2,1%
REM START Masker in the same window using /b tag and create hold.pass:
Echo 0 >%temp%\hold.pass
start /b Masker.bat "%pass%" *
REM Simply take input an leave the rest to Masker.bat
set /p pass_input=:
Echo 1 >>%temp%\hold.pass
cls
if /i "%pass%" NEQ "%pass_input%" (
Title Worng Password
Echo Wrong Password... Sorry.
Sleep 5
Exit
)
Pause > nul
REM Rest of Main game code is below or simply
:: START Main.bat & Exit
Masker.bat
#echo off
Title Password Please:
setlocal enabledelayedexpansion
:: This is not the main code
REM This code is called upon by Login.bat (or the Main.bat game code)
REM CREATE the variables "passlen" and "mask":
set password=%~1
set passlen=0
:miniloop
set /a passlen+=1
if "!password:~%passlen%,1!" NEQ "" goto :miniloop
set password=
set mask=%~2
if "%mask%" EQU "" set mask=*
REM MAIN loop
:loop
cls
for /l %%a in (1,1,%passlen%) do (<nul set /p=%mask%)
sleep -m 150
for /f "usebackq" %%a in ("%temp%\hold.pass") do (if "%%~a" EQU "1" Del %temp%\hold.pass & Exit)
goto :loop
It still needs some more improvements, but I've spent aroung 30 min on it with little success to make it dynamically tell you how many characters you have typed in.
Anyone cane take this up, be my guest. Everything else works fine
Mona
This works without pressing enter after input of the password.
If you enter the correct password, ok.
if you enter a wrong password, it will stop when you enter the 9th character (can be adapted).
It does not care about capitalization.
Problem: the password is stored as pure text in the code
#echo off
setlocal enabledelayedexpansion
set "s= abcdefghijklmnopqrstuvwxyz"
set p=
:loop
choice /C %s% /N >nul
set p=%p%!s:~%errorlevel%,1!&set /p =*<nul
if /i "%p%"=="secured" goto :right
if not "%p:~8,1%"=="" goto :wrong
goto :loop
goto :wrong
:right
echo you entered correct password: %p%
goto :eof
:wrong
echo you entered wrong password: %p%
goto :eof
You may use ReadFormattedLine subroutine for all kind of formatted input. For example, the command below read a password of 8 characters, display asterisks in the screen, and continue automatically with no need to press Enter:
call :ReadFormattedLine password="********" /M "Enter password (8 chars): "
This subroutine is written in pure Batch so it does not require any additional program, and it allows several formatted input operations, like read just numbers, convert letters to uppercase, etc. You may download ReadFormattedLine subroutine from Read a line with specific format.

How to find keywords of the users input in the command line?

What I'm looking for is a batch file line of code that will scan what the user inputs to find key words and direct them in the right place. That way when a trainee has a question he/she could just ask the batch file and it will direct them to the proper menu. Is this possible? if so, How would one go about doing this?
:menu
set /p c=Please type you question:
findstr /m "How to ringout a product on our system?" "%c%"
if %c%=="ringout" (
goto :Ringingout
) Else (
goto :Sorry
)
:Ringingout
cls
echo In order to right something out maksure you do the following:
echo - Log in
echo - click on scan in the bottom left had corner on the tender page
echo - scan items
echo - click continue
Pause
goto :Menu
:Sorry
cls
echo Sorry I don't recognize your question, please re-word it.
pause
goto :Menu
#ECHO OFF
SETLOCAL
SET "keywords=word anotherword someword"
FOR %%k IN (%keywords%) DO set "#%%k="
SET /p query="Please enter your query ? "
FOR %%k IN (%keywords%) DO CALL :analyse %%k
SET #
GOTO :EOF
:analyse
CALL SET "found=%%query:%1=%%"
IF "%found%"=="%query%" GOTO :EOF
SET #%1=FOUND!
GOTO :eof
Here's a general way to do it.
If one of the keywords is entered, the variable #keyword will be set to FOUND! so you can use if defined #keyword to process from there.
It's not protected against destructive user-inputs - that's not what this question is about...
You'd be better off collecting all the questions in a document (like .html) and letting the user search that document for what they need. But if this is just an exercise, you can re-write your logic like so to make your program work:
:menu
set "c="
set /p "c=Please type your question: "
echo %c% | findstr /i /b "ringout" >nul
if errorlevel 1 goto Sorry else goto Ringingout
:Ringingout
and so on.

Resources