how does this batch script works? - batch-file

i got this script for a site but i have some problems how the script works
the script
#setlocal enableextensions enabledelayedexpansion
#echo off
title movement
color 0a
set length=
set height=a
:controls
cls
echo Use WASD to move your character ([]).
echo.
for %%a in ( %height% ) do echo.
echo %length%[]
choice /c wasd /n
if %errorlevel% equ 1 call:up
if %errorlevel% equ 2 call:left
if %errorlevel% equ 3 call:down
if %errorlevel% equ 4 call:right
:left
set length=!length:~0,-1!
goto controls
:right
set length=%length%
goto controls
:up
set height=!height:~0,-2!
goto controls
:down
set height=%height% a
goto controls
ok now can someone explain the first line?
i serached the web and i think it will give value to variables when the command is reached
also i dont know what this means
set height=!height:~0,-2!
and this
set length=!length:~0,-1!

For enabledelayedexpansion see this blog post. (In short it makes variables work in a sane fashion.)
enableextensions seems to be a safety feature in case the command extensions have been disabled (though they appear to be on by default). It also isn't clear to me from a quick read what they are exactly (other than some newer command features).
Edit: Linked from the page #user3245060 mentions in hist comment is the Cmd page which indicates (at least some) commands that are affected by enableextensions and indicates that further details may be available in those commands specific pages. (It would also seem that Noodles has some idea about what is involved here.)
set height=!height:~0,-2! appears to be string processing (as per this link.

Related

variable sharing? Batch CMD

I'm trying to set 2 variables in 3 categories, 6 variables total, copying out the categories three times seems like a poor option especially because my real code is much larger than this with almost 10 categories with 30 variables each.
First I ask which category to set variable (constant) and then asked to set the two variables in that category.
Which is fine, until I want to do something with the combined variable.
#echo off
cls
:start
cls
echo which variable do you want to set?
echo (1),(2),(3)
choice /c 123 /n
if ERRORLEVEL 3 goto :3
if ERRORLEVEL 2 goto :2
if ERRORLEVEL 1 goto :1
:1
set const=one
goto :wizard
:2
set const=two
goto :wizard
:3
set const=three
goto :wizard
:wizard
set /p %const%_varA= set %const% variableA:
set /p %const%_varB= set %const% variableB:
:: this line is the problem
echo %%const%_varA%
echo %%const%_varB%
::
echo.
pause
goto :filewrite
echo.
:filewrite
echo one varA %one_varA%
echo one varB %one_varB%
echo two varA %two_varA%
echo two varB %two_varB%
echo three varA %three_varA%
echo three varB %three_varB%
pause
goto :start
I have played around a bit and the problem you have is the fact that in batch-files the way to escape a % is another one. So your code will eventually look like echo %const%_varA% and as %var_A% is empty/does not exist, the only thing you should be getting is %const as output.
Luckily there is a way to bring another character into the game to prevent this from happening. Adding setlocal EnableDelayedExpansion under the first line will make variables accessable using exclamation marks. This is usually used to access variables in closed sets of parenthesis but comes in handy for this one:
#echo off
setlocal EnableDelayedExpansion
set const=three
set %const%_varA=foo
echo Without exclamationmarks: %%const%_varA%
echo With exclamationmarks : !%const%_varA!
pause
Is a tiny example that demonstrates the problem.
The upper line is what you currently have and not working. The lower one however uses above explaned delayed expansion.
Meaning: First (%) the value of %const% is calculated, changing the line to echo [...] !three_varA! and after (!) comes the whole thing!
Feel free to ask questions :)

BATCH program crashes after goto command

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

Real time keyboard inputs in batch

This link
https://www.youtube.com/watch?v=eULe3DNS8DM
Shows a game made in batch. The game, called viewpoint, allows you to move around and shoot at enemies. This game seems to accept keyboard input in real time to perform actions. Is there any way I can get my batch file to accept keyboard input in real time as well?
(Guessing by the "Core" folder I saw in the game folder, there might be some other program or command involved.)
You could use the choice command (which is what snake.bat uses) however it can only take alpha-numeric input one at a time.
Here is a quick program I whipped up which uses choice to take WASD input.
#setlocal enableextensions enabledelayedexpansion
#echo off
prompt $$$G
title movement
color 0a
set length=
for %%a in (1 2 3) do (
set "length=!length! "
)
:controls
cls
echo Use WASD to move your character ([]).
echo.
echo %length%[]
choice /c wasd /n
if %errorlevel% equ 1 call:up
if %errorlevel% equ 2 call:left
if %errorlevel% equ 3 call:down
if %errorlevel% equ 4 call:right
:left
set length=!length:~0,-1!
goto controls
:right
set "length= %length%"
goto controls
:up
set length=!length:~0,-80!
goto controls
:down
set "length= %length%"
goto controls
The main thing you should focus on is the :controls label and how it uses the choice command to read input.

batch file sub routine

I'm trying to re-use the batch file code in order to perform a similar tasks in a menu pages.
The main menu consists of 10+ options.
When I go inside the each menu items, I need to display a following in text
Press [C] to Continue or [X] to exit [C/X]: _
I created labels in each menu time and re-direct to the code which is responsible for prompting the message and do necessary actions.
How can I use this following code as a subroutine, so that I don't have to re-write the code several times.At the moment I hard code it in each menu item. It would have been easy to call it as a sub routine.
:MiniMenu1
SET INPUT1=
SET /P INPUT1=Press [Y] to Continue Installation or [N] to go back [Y/N]:
IF /I '%INPUT1%'=='y' GOTO Mini_cont1
IF /I '%INPUT1%'=='n' GOTO Mini_back1
ECHO ============INVALID INPUT============
ECHO Please select a number from the Menu Options
ECHO -------------------------------------
ECHO ======PRESS ANY KEY TO CONTINUE======
PAUSE > NUL
GOTO MiniMenu1
Where as my code for main menu item pages are
:Selection1
:: MAin menu item 1
GOTO MiniMenu1
:Mini_cont1
:: xCopy update.zip C:\python27\ /y
#echo Update Completed.
pause
:Mini_back1
:: end
GOTO MENU
Ah - thinking along the right lines. Very good.
#echo off
setlocal
call :ask Question number one
if errorlevel 2 goto Q1X
call :ask Question number two
if errorlevel 2 goto Q2X
::get here for Q1Q2 responses both C
goto :eof
:ask
choice /c CX /N /M "%*"
goto :eof
Here's a basic template. From the prompt, type choice /? for instructions about options.
Hint: set "choices=wqzk" then in the subroutine choice /c %choices% /N /M "%*" would allow you to change the choices available. /n prompts with the available choices, so you've no need to specify that in the text, just make it obvious - Whatever, Quit, Zap, Kill should be obvious for wqzk for instance.
The return in %errorlevel% will the the sequence-number of the character chosen. W==>1, Q==>2..K==>4. In the traditional construct, if errorlevel n the comparison is true if errorlevel is n or greater than n so it would be traditional to use
if errorlevel 4 goto QnA4
if errorlevel 3 goto QnA3
if errorlevel 2 goto QnA2
:: if it gets here, errorlevel is 1 hence choice was first character.
which is shorter than the "modern" way
if %errorlevel%==1 goto QnA1
if %errorlevel%==2 goto QnA2
if %errorlevel%==3 goto QnA3
:: if it gets here, errorlevel is 4 or more hence choice was fourth or later character.
Note: %* means all of the arguments passed to the subroutine so /m "%*" neatly shows the arguments passed as a prompt. There's no voodoo about that. But be careful - text only and a few symbols if you like. Symbols with a special meaning to cmd may cause unexpected results
Variables created/changed/deleted after a setlocal will be deleted/restored/resurrected when a matching endlocal is encountered. Consequently, setlocal is often used as the first "action statement" in a batch - the environment is restored to pristine when the batch ends.
To remove variables within a batch using a subroutine, you could use
call :zap we dont want these variables
:zap
if "%1" neq "" set "%1="&shift&goto zap
goto :eof
(to delete variables we dont want these and variables
or :zap version 2
:zap
for %%a in (%*) do set "%%a="
goto :eof
To remove variables which all start with an identical character-pattern, use
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
(which will remove all variables starting $. $ isn't holy - you could substitute xyz for $ here and zap xyz123 xyz789 and xyzylofone for instance)
Naturally, you could also combine the techniques...
But - it's not expensive to ask a new question on SO. Not expensive at all. Cheap even. Asking a new question rather than tagging more issues onto an existing one makes finding a solution easier (like.. someone wanting to know how to delete variables possibly wouldn't expect to find it under a question titled "batch file sub routine" for instance. It also prevents the question from becoming a saga.

CLS (clear) a single line?

Is there any way to CLS a single line of output? I don't believe there are any switches for CLS, so maybe a better question would be:
Is there any way to
retain all previous output for re-use?
or
capture currently displayed output (like you can by marking and copying)?
I'm just trying to make my scripts a little more user-friendly by having real-time feedback / information, instead of multiple lines with slight changes. The only way I can think of doing this, though, is like this:
#echo off
goto Prep
:Prep
SET count=5
SET genericMessage=This window will close
goto Output
:Output
IF NOT %count% == -1 (
cls
IF %count% == 0 (
echo %genericMessage% now.
) ELSE (
echo %genericMessage% in %count% seconds.
)
SET /A count=%count% - 1
ping localhost -n 2 >nul
goto Output
) ELSE (
exit
)
So, you get this:
The problem with this, though, is that CLS erases all output, when I only want to refresh one line by erasing and re-outputting it.
Anyone have any ideas?
If you only need to move the cursor in one line (like your sample),
it's possible with a carriage return character.
#echo off
setlocal EnableDelayedExpansion
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
for /L %%n in (5 -1 1) do (
<nul set /P "=This window will close in %%n seconds!CR!"
ping -n 2 localhost > nul
)
Unfortunately, there is no native command or utility that repositions your cursor in a Windows command line console.
You will need a 3rd party utility.
Aacini posted a free CursorPos.exe utility on DOSTips. The CurorPos.exe "source" is given as Hex digits. To use the source you will need the HexToBin.bat "compiler".
Browse both threads and you will find a number of utilities you may find useful.
Try ANSI sequences: http://www.robvanderwoude.com/ansi.php
Burrowing down the links, http://batch.xoo.it/t2238-BG-exe-Utility-for-Batch-Games.htm looks the most promising.
This page sounds like it has useful discussion on controlling/setting console sizes (and other display and buffer size settings). http://www.pcreview.co.uk/forums/change-buffer-size-console-window-can-runas-inherit-console-props-t1468842.html
An alternative quick&dirty method of moving the cursor via TIMEOUT:
#echo off
<nul set/p"=5 seconds till i close..."
timeout /t 5 /nobreak >con
echo(i'm closing now...[REPLACE this with lots of spaces]
exit /b

Resources