Changing variable values within If statements - windows

I'm trying to take some user input defined variables to decide whether or not to enter the loop and execute a copy and rename followed by deletion of the original file as it will no longer be needed.
set /p multiTune="Does your tune file need to be shared with multiple
Element sequences? (y/n) "
Echo Multi Tune is %multiTune%
if "%multiTune%"=="y" (set /p tuneCount="How many sequences will need to share your tune file? ")
Echo Tune count is %tuneCount%
pause
if "%multiTune%"=="y" (SET /p tuneName="Enter the file letter/number combination for the R quant of your tune file. ") else (#ECHO The user specified there is no need for a second tune.)
Echo Tune Name is %tuneName%
pause
if "%multiTune%"=="y" (SET /a tuneCount+=1) else(set /a tuneCount-=tuneCount)
Echo Tune count is now %tuneCount%
pause
:loop
if "%tuneCount%"=="0" goto exitloop
Set /p seqNumber="Enter the number for one of the sequences."
copy %tuneName%.D.pdf "S%seqNumber%-TUN1_%tuneName%.D.pdf"
echo %tuneName%.D.pdf renamed to S%seqNumber%-TUN1_%tuneName%.D.pdf
pause
Set /a tuneCount-=1
if "%tuneCount%"=="1" DEL "%tuneName%.D.pdf"
if "%tuneCount%"==1 goto exitloop
Echo %tuneCount%
pause
goto loop
:exitloop
All of the echos and pauses are just for testing purposes to make sure I have the correct values in my variables.
The batch file runs fine with the variables containing the correct strings and values up until the line:
if "%multiTune%"=="y" (SET /a tuneCount+=1) else(set /a tuneCount-=tuneCount)
The file says something is unexpected and closes at this point so i havent gotten a chance to figure out if the looped portion even works. The point of the +1 is so that it enters the loop and executes the commands until it gets to 1 and skips the loop if it equals 0.
I read a bunch of information about setlocal delayedexpansion and using !'s around variables instead of %'s. I'm not sure how to implement this or if this applies to my problem at all. I know there is probably an easier way to do the if statements but I'm a novice and that was the easiest way for me to understand it as I've been learning on the fly through trial and error, and everything you see is the results of a single day of learning.
Any help would be much appreciated. I tried to be as detailed as possible about what it is I'm trying to do but if you have any questions I will do my best to answer.

I really think you are making the things so MUCH complicated...
Have a corrected piece of the code you provided us (Note: I did not touch the loop subroutine to be on-topic):
#echo off
choice /m "Does your tune file need to be shared with multiple element sequences? (y/n) " /C:yn /N
rem Echo Multi Tune is %errorlevel%
rem If errorlevel equals to 1 user input is "Y", it is 2 it is "N". (I commented the "echo" command as it changes the errorlevel value).
if errorlevel 2 goto question_N
if errorlevel 1 goto question_Y
:question_Y
set /p tuneCount="How many sequences will need to share your tune file? "
set /p tuneName="Enter the file letter/number combination for the R quant of your tune file. "
SET /a "tuneCount+=1"
goto loop
:question_N
set /p tuneCount="How many sequences will need to share your tune file? "
ECHO The user specified there is no need for a second tune.
set /a "tuneCount-=tuneCount"
goto loop
:loop
rem [Code you provided above]
I hope you are fine with this, testing it and it works!

Related

My script wont redirect to my other classes?

I created a variable and I assigned it to an input. But no matter what I input it just redirects back to :EditUser. I don't know why it is doing that.
This is what my code is. Can someone tell me why it wont direct to the other classes. This is the only redirection in my script that won't work.
:EditUser
cls
echo You chose to edit a user
echo ========================
echo 1. Change Password
echo 2. Change Account Type
echo 3. Go back to Users
echo.
set /p CHOICE6=What do you want to do?
if CHOICE6==1 goto PassChange
if CHOICE6==2 goto PrivChange
if CHOICE6==3 goto Users
goto EditUser
You forgot To wrap your variables with % to make recognise them as such. It should be %CHOICE6% Additionally, you should either enclose the if statement query in double quotes to eliminate possible whitespace if "%CHOICE6%"=="1" or even better seeing as it is numerical, use if %CHOICE6% equ 1
Anyway, seeing as you are working with the obvious of Choice then just use choice instead of set /p
:EditUser
cls
echo You chose to edit a user
choice /c 123 /m "Select (1) Change Password (2) change account type (3) go back to users"
goto :opt%errorlevel%
:opt1
echo change password stuff here.
goto :eof
:opt2
echo change account type stuff goes here
goto :eof
:opt3
goto :users
goto :eof
So what Really happens is that we just goto a label starting with opt appended by the errorlevel returned by choice
It also only allows one of the alphanumeric values entered only, anything else you type will not be acted on. So in this case, only 1, 2 or 3 will be acted on by cmd.
see choice /? for more detail.
Also, if you want to retain your echo layout and not have the options in one line, add the echo's and remove the message /m swirtch from choice:
:EditUser
cls
echo You chose to edit a user
echo ========================
echo 1. Change Password
echo 2. Change Account Type
echo 3. Go back to Users
echo(
choice /c 123
goto :opt%errorlevel%
:opt1
echo change password stuff here.
goto :eof
:opt2
echo change account type stuff goes here
goto :eof
:opt3
goto :users?
goto :eof
You simply forgot to wrap your variables in % when using them.
however, you woudl do well to use the good practice of wrapping SET commands in " as well
Also, you could simplify your life a little by using choice, although it's still possible to get the choice to error out so YMMV
:EditUser
cls
echo.You chose to edit a user
echo.========================
echo.1. Change Password
echo.2. Change Account Type
echo.3. Go back to Users
echo.
set /p "CHOICE6=What do you want to do? "
IF %CHOICE6% EQU 1 goto :PassChange
IF %CHOICE6% EQU 2 goto :PrivChange
if %CHOICE6% EQU 3 goto :Users
goto :EditUser
That said for best practice you should write functions using CALL and you can return choice variables to the main code to act on or do it from within the function as suites best.
CALL is "SAFE" as it will return to the current point of execution on a function or script ending, and you can write scripts in a better practice.
REM Example main function
:Main
REM ...
REM Call Edit User, Specify a variable name to be returned
CALL :EditUser "CALL_THIS"
REM Call the function we chose in the :Edit User Function and was returned to us here, and may be :PassChange, :PrivChange, :Users
CALL %CALL_THIS%
REM ...
GOTO :EOF
:EditUser
REM Clear the Variable Provided to the fucntion to be returned
SET "%~1="
REM Clear the Choice
SET "CHOICE6="
cls
echo.You chose to edit a user
echo.========================
echo.1. Change Password
echo.2. Change Account Type
echo.3. Go back to Users
echo.
CHOICE /C 123 /N /M "What do you want to do? "
SET "CHOICE6=%ERRORLEVEL%"
REM Check an arbitrary number of choices without having to write a full IF on each.
FOR %%_ IN (
1:PassChange
2:PrivChange
3:Users
) DO (
REM Split choices to test them:
FOR /F "Tokens=12 Delims=:" %%A (
IF %%A EQU %CHOICE6% (
REM Set The Variable given the function '%1' to a value to return to Main script when the Choice was matched.
SET "%~1=:%%B"
)
)
REM Exit Function when %~1 is defined.
IF DEFINED %~1 GOTO :EOF
)
REM Show screen again when not matched
GOTO :EditUser
Yes more code but as you can see now you have a reusable function that could be called an arbitrary number of times as your code requires, and allows the outer code whether in another function or in the main code to execute the next called function.
But even in places where you don't need to call another function you can use the loop logic here to store a list of choices and evaluate them without having to re-type all fo your if statements.
Further, you could write that more generically too and not have any multiple-choice variables re-using the choice function logic each time as the callable function, and allowing you to write less repetitive and smaller footprint code.

Why wont this batch file work? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I was trying to create a simple program that would show up your name after you entered it.
After spending a few hours trying to get this batch file to work on a windows 10 and windows 7 computers, I can't still figure out what is the problem. After you input your answer whether or not you confirm that that is your name, it is still not working.
I've tried to debug by putting pause just about everywhere and it continues not to work.
Can somebody please point out what is wrong? Thanks.
Here is my code:
ECHO OFF
setlocal
COLOR A
cls
:getName
ECHO test
echo Please input name.
set "name="
SET /P NAME=
if not defined NAME goto getName
ECHO %NAME%, is this correct? Y/N
set /p 097=
if %O97%==Y goto :begin
set favvid=0
set hack=0
:b
echo Input name
set name=
set /P name=
echo %name%, correct? Y/N
set 897=
set /p 897=
if %897%==N goto :c
if %897%==Y goto :begin
:c
echo Input name
set name=
set /P name=
echo %name%, correct? Y/N
set 897=
set /p 897=
if %897%==N goto b
if %897%==Y goto begin
echo Name = %NAME% Is now your name. Too many attempts
:begin
echo Hello %Name%
pause
Avoid starting variable names with a number, this will avoid the
variable being misinterpreted as a parameter:
%123_myvar% in a batch script is parsed and then executed as %1 23_myvar
For proof, force echo ON.
And use (note that variable first character is not cipher zero but letter O.
set /p "O97=%name%, correct? Y/N "
if "%O97%"=="Y" goto :begin
rem note quoting in above commands
Consider using the CHOICE command as an alternative to SET /P (but accepts only one character/keypress).
Two errors on the if lines:
You should not use numbers to start variables in a batch file as they are interpreted as the arguments passed to the batch file. %0 is the batch file itself. %1 is the first, %8 is the 8th argument etc. (You can try echo --%0--%1--%097%-- in the batch file to see what result it gives you.)
Instead of this if "%097%"=="Y" goto begin
Use this if "%Answer%"=="Y" goto begin
Now also note that while your line set /p 097= has 097 when you
test for it in the next line with %O97% you do not have a 0 but
you have the letter O. This makes your test fail every time.
You need to put the string in quotes, like so:
if "%Answer%"=="Y" goto begin
Finally note that the test is case sensitive. So when the user presses y it will be interpreted as the wrong answer. You need to add a /I to your test:
if/I"%Answer%"=="Y" goto begin
One more point. After the goto you put colon some places, some places not, it is facultative but it is neater to keep it consistent.

Batch File not working for the given input

Following is the batch file that I have created, for any input its only showing is "NO INPUT PROVIDED" string, I tried searching on google and tried many things but non solved my problem.
#echo off
ECHO Please provide input... Valid input is :: Y/y (For changing path) or N/n (For not changing the path).
SETLOCAL
SET /p change=
if "%~1" equ "" GOTO ERROREND
if /I "%change%" equ "Y" GOTO YES
if /I "%change%" equ "N" GOTO NO
:YES
ECHO Y SELECTED.
GOTO SUCCESS
:NO
ECHO N SELECTED.
GOTO SUCCESS
:ERROREND
ECHO Input not recognized.
GOTO FAILURE
:SUCCESS
ECHO Task completed succcess fully.
pause
:FAILURE
ECHO NO INPUT PROVIDED.
pause
Help needed. Thank you for reading this question.
if "%~1" equ "" GOTO ERROREND
means 'if the first parameter to the batchfile is empty', so if you were to run this batch from the prompt as
yourbatchname something
then something is the first parameter.
You probably menat to use "%change%", not `"%~1".
Note: if not defined change is probably a better test.
Note: Simply pressing Enter when prompted by a set /p does not set the variable to empty. It leaves the value unchanged. Best to use
set "var="
set /p var=....
Note: batch simply charges on, instruction by instruction. It has no concept of 'end-of-procedure'. Consequently, if your entry is neither Y nor N (once you've got the %~1 issue resolved) batch will simply continue execution past the if statements to the next - the ECHO Y SELECTED.

Windows Command line script decrementing variables in FOR loop based on a user defined starting variable

I'm trying to write what seemed like an easy script but I can't figure it out.
Basically, a user is asked Question 1: "How many (in this case) video files they want to add together to create 1 big video file?"
The user then gets asked Question 2: "what is the name of the file you want to add together?" Now here is the problem I'm having...
How do I create a for loop that asks that Question 2 the amount of times given in the 1st question and saves each answer as a unique variable (I'm guessing at decrementation of the variable)
after I have all the correct file names from the user then the program will call the video program according to the video program syntax (THAT syntax I don't need help with, I understand that part)
ex. (a "?" means i don't know what to put there)
#echo off
set /p howmany=How many files do you want to add?
for /? %%variable(???) in (%howmany%???) do (set /p inputfilename=what is the name of the first file you want to add? inputfilename=filename set %howmany%-1???=%howmany%????)
so if the user answered 5 to the Question 1, then the for loop should ask Question 2 five times and create 5 unique variables for every time the answer is given. inputfilename1 = movie1.mov inputfilename2 = movie2.mov etc..
I've been trying to figure this out for a few days.. I can't rap my head around it. I've done plenty of for commands before but this has got me stumped. My browser history is full of google searches that seems like something people would ask about any kind of files. If I did find anything remotely close to this question it was always asked for a different programming language. My brain is fried. Is this even something possible? Please help and thanks in advance.
Although Martin's answer describe how to create the unique variables, he didn't explained how to read them. When you are talking about "saves each answer as a unique variable" the involved concept here is ARRAY. You need to use Delayed Expansion in order to get the values of the unique variables ("array elements"); for further details, type set /? and look for "delayed expansion". You may read a detailed description about array management in Batch files at this post: Arrays, linked lists and other data structures in cmd.exe (batch) script
#echo off
setlocal EnableDelayedExpansion
set /p howmany=How many files do you want to add?
for /L %%i in (1,1,%howmany%) do (
set /p inputfilename[%%i]=what is the name of the file you want to add?
)
rem Process array elements (just show them in this case)
for /L %%i in (1,1,%howmany%) do (
echo %%i- !inputfilename[%%i]!
)
The example below may help you in understanding array management in an easier way:
#echo off
setlocal EnableDelayedExpansion
rem Create an array of ordinal terms
set i=0
for %%a in (first second third fourth fifth sixth) do (
set /A i+=1
set term[!i!]=%%a
)
rem Previous FOR is equivalent to: set term[1]=first, set term[2]=second, ...
set /p howmany=How many files do you want to add?
for /L %%i in (1,1,%howmany%) do (
set /p inputfilename[%%i]=what is the name of the !term[%%i]! file you want to add?
)
rem Process array elements (just show them in this case)
for /L %%i in (1,1,%howmany%) do (
echo The !term[%%i]! file is !inputfilename[%%i]!
)
Anyway to answer your actual question:
#echo off
set /p howmany=How many files do you want to add?
for /L %%i in (1, 1, %howmany%) do (
set /p inputfilename%%i=what is the name of the first file you want to add?
)
rem Output the variables to check
set inputfilename
Output:
How many files do you want to add? 3
what is the name of the first file you want to add? first
what is the name of the first file you want to add? second
what is the name of the first file you want to add? third
inputfilename1=first
inputfilename2=second
inputfilename3=third
What do you need those N variables for? I would guess you need to pass a list of filenames to some script/command-line application.
So, wouldn't you better do with one variable with (space?-)delimited list of filenames?
Like:
#echo off
set /p howmany=How many files do you want to add?
set list=
:NEXT
if %howmany% leq 0 goto END
set /p inputfilename=what is the name of the first file you want to add?
set list=%list% "%inputfilename%"
set /a howmany=%howmany% - 1
goto NEXT
:END
echo %list%

Changing a batch file when its running

I am running a long running batch file. I now realize that I have to add some more commands at the end of the batch file (no changes to exisiting content, just some extra commands). Is it possible to do this, given that most batch files are read incrementally and executed one by one? Or does the system read the entire contents of the file and then runs the job?
I just tried it, and against my intuition, it picked up the new commands at the end (on Windows XP)
I created a batch file containing
echo Hello
pause
echo world
I ran the file, and while it was paused, added
echo Salute
Saved it and pressed enter to contine the pause, all three prompts were echoed to the console.
So, go for it!
The command interpreter remembers the line position byte offset it's at in the batch file. You will be fine as long as you modify the batch file after the current executing line position byte offset at the end of the most recently parsed line of code.
If you modify it before then it will start doing strange things (repeating commands etc..).
jeb's example is a lot of fun, but it is very dependent on the length of the text that is added or deleted. I think the counter-intuitive results are what rein meant when he said "If you modify it before then it will start doing strange things (repeating commands etc..)".
I've modified jeb's code to show how dynamic code of varying length can be freely modified at the beginning of an executing batch file as long as appropriate padding is in place. The entire dynamic section is completely replaced with each iteration. Each dynamic line is prefixed with a non interfering ;. This conveniently allows FOR /F to strip the dynamic code because of the implicit EOL=; option.
Instead of looking for a particular line number, I look for a specific comment to locate where the dynamic code begins. This is easier to maintain.
I use lines of equal signs to harmlessly pad the code to allow for expansion and contraction. Any combination of the following characters could be used: comma, semicolon, equal, space, tab and/or newline. (Of course the padding cannot begin with a semicolon.) The equal signs within the parentheses allow for code expansion. The equal signs after the parentheses allow for code contraction.
Note that FOR /F strips empty lines. This limitation could be overcome by using FINDSTR to prefix each line with the line number and then strip out the prefix within the loop. But the extra code slows things down, so it's not worth doing unless the code is dependent on blank lines.
#echo off
setlocal DisableDelayedExpansion
echo The starting filesize is %~z0
:loop
echo ----------------------
::*** Start of dynamic code ***
;set value=1
::*** End of dynamic code ***
echo The current value=%value%
::
::The 2 lines of equal signs amount to 164 bytes, including end of line chars.
::Putting the lines both within and after the parentheses allows for expansion
::or contraction by up to 164 bytes within the dynamic section of code.
(
call :changeBatch
==============================================================================
==============================================================================
)
================================================================================
================================================================================
set /p "quit=Enter Q to quit, anything else to continue: "
if /i "%quit%"=="Q" exit /b
goto :loop
:changeBatch
(
for /f "usebackq delims=" %%a in ("%~f0") do (
echo %%a
if "%%a"=="::*** Start of dynamic code ***" (
setlocal enableDelayedExpansion
set /a newValue=value+1, extra=!random!%%9
echo ;set value=!newValue!
for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n
endlocal
)
)
) >"%~f0.tmp"
::
::The 2 lines of equal signs amount to 164 bytes, including end of line chars.
::Putting the lines both within and after the parentheses allows for expansion
::or contraction by up to 164 bytes within the dynamic section of code.
(
move /y "%~f0.tmp" "%~f0" > nul
==============================================================================
==============================================================================
)
================================================================================
================================================================================
echo The new filesize is %~z0
exit /b
The above works, but things are much easier if the dynamic code is moved to a subroutine at the end of the file. The code can expand and contract without limitation, and without the need for padding. FINDSTR is much faster than FOR /F at removing the dynamic portion. Dynamic lines can be safely be prefixed with a semicolon (including labels!). Then the FINDSTR /V option is used to exclude lines that begin with a semicolon and the new dynamic code can simply be appended.
#echo off
setlocal DisableDelayedExpansion
echo The starting filesize is %~z0
:loop
echo ----------------------
call :changeBatch
call :dynamicCode1
call :dynamicCode2
echo The current value=%value%
set /p "quit=Enter Q to quit, anything else to continue: "
if /i "%quit%"=="Q" exit /b
goto :loop
:changeBatch
(
findstr /v "^;" "%~f0"
setlocal enableDelayedExpansion
set /a newValue=value+1, extra=!random!%%9
echo ;:dynamicCode1
echo ;set value=!newValue!
echo ;exit /b
echo ;
echo ;:dynamicCode2
for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n
echo ;exit /b
endlocal
) >"%~f0.tmp"
move /y "%~f0.tmp" "%~f0" > nul
echo The new filesize is %~z0
exit /b
;:dynamicCode1
;set value=33
;exit /b
;
;:dynamicCode2
;echo extra line 1
;exit /b
Short answer: yes, batch files can modify themselves whilst running. As others have already confirmed.
Years and years ago, back before Windows 3, the place I worked had an inhouse menu system in MS-DOS. The way it ran things was quite elegant: it actually ran from a batch file that the main program (written in C) modified in order to run scripts. This trick meant that the menu program itself was not taking up memory space whilst selections were running. And this included things like the LAN Mail program and the 3270 terminal program.
But running from a self-modifying batch file meant its scripts could also do things like load TSR programs and in fact could do pretty much anything you could put in a batch file. Which made it very powerful. Only the GOTO command didn't work, until the author eventually figured out how to make the batch file restart itself for each command.
Nearly like rein said, cmd.exe remember the file position (not only the line position) it's currently is, and also for each call it push the file position on an invisble stack.
That means, you can edit your file while it's running behind and before the actual file position, you only need to know what you do ...
A small sample of an self modifying batch
It changes the line set value=1000 continuously
#echo off
setlocal DisableDelayedExpansion
:loop
REM **** the next line will be changed
set value=1000
rem ***
echo ----------------------
echo The current value=%value%
<nul set /p ".=Press a key"
pause > nul
echo(
(
call :changeBatch
rem This should be here and it should be long
)
rem ** It is neccessary, that this is also here!
goto :loop
rem ...
:changeBatch
set /a n=0
set /a newValue=value+1
set /a toggle=value %% 2
set "theNewLine=set value=%newValue%"
if %toggle%==0 (
set "theNewLine=%theNewLine% & rem This adds 50 byte to the filesize.........."
)
del "%~f0.tmp" 2> nul
for /F "usebackq delims=" %%a in ("%~f0") DO (
set /a n+=1
set "line=%%a"
setlocal EnableDelayedExpansion
if !n!==5 (
(echo !theNewLine!)
) ELSE (
(echo !line!)
)
endlocal
) >> "%~f0.tmp"
(
rem the copy should be done in a parenthesis block
copy "%~f0.tmp" "%~f0" > nul
if Armageddon==TheEndOfDays (
echo This can't never be true, or is it?
)
)
echo The first line after the replace action....
echo The second line comes always after the first line?
echo The current filesize is now %~z0
goto :eof
The command interpreter appears to remember the byte offset within each command file it is reading, but the file itself is not locked, so it is possible to make changes, say with a text editor, whilst it is running.
If a change is made to the file after this remembered location, the interpreter should happily continue to execute the now modified script. However if the change is made before that point, and that modification changes the length of the text at that point (for example you've inserted or removed some text), that remembered location is now no longer referring to the start of that next command. When the interpreter tries to read the next 'line' it will instead pick up a different line, or possibly part of a line depending on how much text was inserted or removed. If you're lucky, it will probably not be able to process whatever word it happen to land on, give an error and continue to execute from the next line - but still probably not what you want.
However, with understanding of what's going on, you can structure your scripts to reduce the risk. I have scripts that implement a simply menu system, by displaying a menu, accepting input from the user using the choice command and then processing the selection. The trick is to ensure that the point where the script waits for input is near the top of the file, so that any edits you might wish to make will occur after that point and so have no nasty impacts.
Example:
:top
call :displayMenu
:prompt
REM The script will spend most of its time waiting here.
choice /C:1234 /N "Enter selection: "
if ERRORLEVEL == 4 goto DoOption4
if ERRORLEVEL == 3 goto DoOption3
if ERRORLEVEL == 2 goto DoOption2
goto DoOption1
:displayMenu
(many lines to display menu)
goto prompt
:DoOption1
(many lines to do Option 1)
goto top
:DoOption2
(many lines to do Option 2)
goto top
(etc)

Resources