This code is part of a chat program that I am currently working on. The 'else' part of my program is the one that doesn't work. The program quits instead of going to :home
:join
cls
if not exist "C:/Users/Public/room.cmd" (
echo No room has been found.
echo.
set /p choiceretry=Do you want to retry? y/n
if "%choiceretry%"=="y" goto join
if "%choiceretry%"=="n" goto home
) else (
cls
"C:/Users/Public/room.cmd"
echo A room has been found.
pause >nul
echo Joining
set roomjoined=1
echo %roomjoined%
goto home
)
:home
echo this finally works
pause
I have tried changing the code several times starting from 'echo Joining'
Anyone know why cmd quits?...
:) :) :)
Thanks in advance
The problem is the way you run room.cmd; you must use call to return from it:
call "C:/Users/Public/room.cmd"
Otherwise, execution will not return from room.cmd to the original batch file that ran it.
Hint: Consider to use choice instead of set /P for Y/N decisions.
Firstly, please don't left justify your code blocks. It's much easier to read code that's properly indented.
Secondly, when retrieving values within a code block, you need delayed expansion. See setlocal /? in a cmd prompt for more information. This is the reason for the unexpected behavior. Your variables retrieved within the same parenthetical code block in which they were set won't contain the values you expect unless you retrieve them with delayed expansion syntax. As an alternative, you could use the choice command and if errorlevel, which would result in a bit nicer user experience I think.
Thirdly, when testing user input, you should use the /i switch in your if statements for case-insensitivity. This isn't relevant if using choice / if errorlevel though.
Fourthly, Windows paths use backslashes, not forward slashes.
I'd fix it this way:
#echo off
setlocal
:join
cls
if errorlevel 1 set /P "=Retrying... "<NUL
if not exist "C:\Users\Public\room.cmd" (
echo No room has been found.
echo.
choice /c yn /n /m "Do you want to retry? [y/n] "
if errorlevel 2 goto home
goto join
) else (
"C:\Users\Public\room.cmd"
echo A room has been found.
pause >nul
echo Joining
set roomjoined=1
)
:home
echo this finally works
pause
Related
Well I have a simple for loop to take user input and creat a file with this input, then ask for the next input and create "file2" etc., but I can't seem to figure out how my user can quit the loop with a certain input (maybe "exit" or "end").
setlocal enabledelayedexpansion
set x=1
:runName
set /p names="Enter header for file !x!:"
for %%a in (%names%) do (>>file!x!.txt (echo %%~a) set /a x+=1)
if !names!=end goto:eof else goto:runName
:eof
echo press any key to close
pause>nul
Ps. Sorry for the poor formatting, I cant figure how to enter my code in the grey field from my phone and dont have a computer at the moment.
I'm not quite shure why the input prompt says header.
Here is my best guess what you may have meant:
#Echo off
setlocal enabledelayedexpansion
set x=1
:runName
Set "names="
set /p names="Enter header for file%x%:"
if not defined names goto :end
if /I "%names%" Equ "end" goto :end
(
for %%a in (%names%) do (echo:%%~a)
) >file!x!.txt
set /a x+=1
goto :runName
:end
echo press any key to close
pause>nul
Running
Enter header for file1:anton bertha caesar
Enter header for file2:anchorage berlin
Enter header for file3:END
press any key to close
File content
> type file*.txt
file1.txt
anton
bertha
caesar
file2.txt
anchorage
berlin
I can't seem to figure out how my user can quit the loop
If you'll allow a suggestion, mine would be don't do that.
Almost any batch script is best written to accept command-line options and files as input. Once you trap the user into providing input interactively -- anything more than Y or N, and even that's a nuisance -- you're putting him in a bad spot. He can't re-run your script without going through the tedium again. He can't use tab completion, or pick from a list. He's trapped in a user-interface element that hasn't been improved in 30 years, and wasn't very good then.
I don't know what to suggest specifically. The example you provide is so trivial it would be easier to provide the file directly using Notepad than to create it with a script. Then you might want a verification script to make sure the provided input file is valid.
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
for /f "SKIP=0 EOL=; TOKENS=* DELIMS=" %%i in ('dir /b c:\windows\*.* ^|FIND /i *.exe') do (
CHOICE /D N /T 5 /C YN /M "Please pre Y to exit"
IF !ERRORLEVEL!==1 GOTO :END
IF !ERRORLEVEL!==2 ECHO CONTINUE
timeout 5
#echo %%i
)
:END
ENDLOCAL
I have always tried to do this but have resorted to entering this in the command line:
CHKDSK C: /f
I tried to look up how to do it but im not that big on bat file programming and more so am just a small programmer learning java atm...
I wanted to know how to do this and more so know how to compute it. As it would help me in the long term and also help me in the short term.
Would be much appreciated. :)
Using 'choice /c' command
This script will ask the user which drive to fix, and then show a confirmation message before 'fixing' the drive:
#echo off
:start
setlocal EnableDelayedExpansion
set letters= abcdefghijklmnopqrstuvwxyz
choice /n /c %letters% /m "Please enter the drive letter you would like to fix: "
set drv=!letters:~%errorlevel%,1!
echo Are you sure... to fix %drv%:\?
choice
if errorlevel 2 goto :start
chkdsk %drv%: /f
echo Complete!
pause
Using 'set /p' command
This script is easier to write and understand, but it shouldn't be used:
#echo off
:start
:: Clears the contents of the %drv% variable, if it's already set
set "drv="
:: Queries the user for input
set /p "drv=Please enter the drive letter you would like to fix: "
:: Check if input was blank
if "%drv%"=="" echo Don't leave this blank&goto :start
:: Check if input contained more then 1 letter (Doesn't account for numbers or special characters)
if not "%drv:~1,1%"=="" echo Please enter the drive letter&goto :start
echo Are you sure you want to fix %drv%:\?
choice
if errorlevel 2 goto :start
chkdsk %drv%: /f
echo Complete!
pause
There are two issues with the batch below. The first is, when batch file when opened prompts the user with a "y/n" question:
Question 1
Has the check been done
If the answer to this is "y" then another "y/n" question is displayed
Question 2
Do you want to send the DOSE report
If the answer to question 1 is "n" the check function is called and another question is displayed. However, currently the line in bold is being displayed then it is going to the second part (the goodbye function). What am I doing wrong? Thank you :).
Current batch
#ECHO OFF
:: ask user
:choice
set /P c=Has the check been done [y/n]
if /i %c%==y (
set /P c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
if /i %c%==y (
"L:\NGS\HLA LAB\total quality management\QC & QA\DOSE reports\DOSE reporting form.xlsm"
) else (
if /i %c%==n goto goodbye
)
:check
set /P c=Do you want to perform the check [y/n]
if /i %c%==y (
echo "perform check and hit enter when complete"
pause goto choice
) else (
if /i %c%==n goto goodbye
:: count loop
set var1=0
:loop
set /a var1=%var1%+1
echo %var1%
if %var1% EQU 1 (
goto end
) else (
goto loop
)
:end
echo "the DOSE report has already been sent by %USERNAME% on %DATE% at %TIME%"
:goodbye
echo "goodbye"
TIMEOUT 2 /nobreak
exit
First off, the if-else statement in :choice is missing its closing )
Heres an important note on user input, never trust it. Try putting something other than y/n in the first
:choice
set /p c=Has the check been done [y/n]
if /i %c%==y (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
If you input something invalid into the first if-else , say somebody tries typing no instead of n, it will fail to return them to :choice as it only checks for n or y
And end up running through script. In your case it fails the if statements before :check and starts :check's proccess, but in check the same issue arises, and it will run through to ::count loop and the following commands where it can mess up your data.
After each if-else statement, its VERY safe practice to add a default action, such as;
:choice
set /p c=Has the check been done [y/n]
if /i %c%==y (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
:[ If both if statements are false, it will reach this line: ]
cls
echo Error: "%c%" is not y/n.
pause
goto :choice
Another thing to note, if nothing is input, errors will occur. You can fix this by checking if the variable is defined right after set /p c=Has the check been done [y/n] using:
if not defined c (
cls
echo Error: Input cannot be empty!
pause
goto :choice
)
So a proper way to do the first check would be:
:choice
set /p c=Has the check been done [y/n]
:[Empty check ]
if not defined c cls & echo Error: Input cannot be empty! & pause & goto :choice
if /i "%c%==y" (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i "%c%==n" goto check
)
:[ Invalid input ]
cls & echo Error: "%c%" is not y/n. & pause & goto :choice
To answer your question directly... well, I'm not sure what you're asking to be honest. I'm having trouble deciphering "I am trying to have the below batch file increment the counter %var1 only if the "y" and not able to get the syntax correct."
I don't see any outright syntax errors, but you do have quite a few logic issues with your script. Indeed, the value of %var1% is being incremented. Put an echo Var1 incremented: %var1% after the set /a var1+=1 and see for yourself. To fix your script, you need to address what statements get executed under what conditions.
Use setlocal. It's just good practice. Using setlocal helps you avoid contaminating the outer console thread with variables that are only relevant to the runtime of this script.
Don't trust that the user will always answer "y" or "n". What happens if they answer "w"? As your script is written, it will proceed to sections you probably didn't intend. I have two suggestions for this.
a. Don't explicitly check for an answer of "n". If it's not "y", assume it's "n". If that is not appropriate, then have a final else goto label for whatever label immediately precedes the conditional.
b. Alternatively, instead of set /P, consider using choice. Example:
choice /c YN /m "Do you want to perform the check? "
if errorlevel 2 (
rem // user chose "N"
) else (
rem // user chose "Y"
)
In your :choice section of code, what is the difference between an answer of Y then N, versus an answer of N? As written, there's no difference. Either way, the script proceeds to :check. Examine your logic here.
Instead of exit, consider using either exit /b or goto :EOF. When running your script from a cmd prompt (as opposed to double-clicking it), you should avoid exiting the parent thread.
For goodness sake, indent your code and add some line breaks! That big blob of left-justified commands has no flow, no rhythm. It's tedious to read and troubleshoot.
Before:
#ECHO OFF
:: ask user
:choice
set /P c=Has the check been done [y/n]
if /i %c%==y (
set /P c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
:check
set /P c=Do you want to perform the check [y/n]
if /i %c%==y (
set /P c=please complete the check and click enter
goto file
) else (
if /i %c%==n goto goodbye
)
It's like a slab, difficult to see at a glance which statements are ancestors and which are descendents or where the section breaks occur.
After:
#ECHO OFF
setlocal
:choice // ask user
choice /c YN /m "Has the check been done? "
if not errorlevel 2 (
choice /c YN /m "Do you want to send the DOSE report? "
if errorlevel 2 (
goto goodbye
)
)
:check
choice /c YN /m "Do you want to perform the check? "
if errorlevel 2 goto goodbye
set /P "c=Please complete the check and click enter: "
See? It's much more pleasant to read that way. By the way, you might've noticed that I combined the "ask user" comment with the :choice label. That's often done when defining functions to explain expected parameters, or just explain what's going on. You don't really need the double slashes. It just makes Stack Overflow's Ted Turner Technicolor parser demonstrate that that's a comment.
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.
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.