How can I make the sum of the imputed numbers given by the user from the keyboard ? I tried a for loop but is endless. I cannot figure out a break statement.
::#ECHO OFF
setlocal EnableDelayedExpansion
set /p X=
SET /A suma=0
:Loop
FOR %%G IN (%X%) DO (
IF %%G GTR 0 (
SET /A suma=%suma%+%%G
)
)
SHIFT
GOTO Loop
:completed
echo %suma%
What i want to do is to make the program more user friendyl like echo somthing for the user to know it is supposed to write somthing. I know the correct code for this question, but the numbers are given as parameters in the command (syntax: script.bat parameter1 parameter2 parameter3).
::#ECHO OFF
SET /A suma=0
:Loop
IF "%1"=="" GOTO completed
FOR %%F IN (%1) DO (
IF %1 GTR 0 (
SET /A suma=%suma%+%1
)
)
SHIFT
GOTO Loop
:completed
echo %suma%
Please give me any idea for the breaking statement in the first example or how can I modify the code to take the numbers from the users imput variable and not as paramaters.
Thank you.
SET /A suma=0
set "X=%*"
if not defined X set /p "X=No parameters detected "
if not defined X ECHO No user-input detected&goto completed
FOR %%G IN (%X%) DO (
IF %%G GTR 0 (
SET /A suma=suma+%%G
)
)
:completed
echo %suma%
%* means the command-line tail. If no tail, then ask user for input. If still no data, issue message and show result.
shift moves the command line tail one position, so %2 becomes %1, etc. You haven't provided a test to exit the routine - and the command-line tail is presumably empty, so you are shifting it (which does nothing) and looping.
The for...%%G.. processes the entire list %X%, so there's no point in the shift/loop - the data has already been processed.
Related
I have this batch file here and it doesn't run well. It says "( was unexpected at this time". Do you know why?
#ECHO OFF
SET A[0]=HOLA
SET A[1]=HELLO
SET I=0
:LOOP
IF DEFINED A[%I%] (
ECHO %I%
CALL SET B=%%A[%I%]%%
CALL ECHO %%B%%
IF %B% == HOLA (
ECHO ES
) ELSE (
ECHO EN
)
PAUSE
SET /A I+=1
GOTO :LOOP )
My expected output should be:
0
HOLA
ES
1
HELLO
EN
I don't understand why inside IF everything works different. Thanks in advance.
#ECHO OFF
SETLOCAL
SET "A[0]=HOLA"
SET "A[1]=HELLO"
SET /a I=0
:LOOP
IF DEFINED A[%I%] (
ECHO %I%
CALL SET "B=%%A[%I%]%%"
CALL ECHO %%B%%
FOR /f "delims==" %%b IN ('set B') DO IF /i "%%b"=="B" (IF "%%c"=="HOLA" (SET "Same=Y") ELSE SET "Same=")
IF DEFINED Same (
ECHO ES
) ELSE (
ECHO EN
)
rem PAUSE
SET /A I+=1
GOTO LOOP
)
GOTO :EOF
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
Your set syntax sets the value of the variable to the rest of the line.
Hence, the line including the label loop would be assigned to i.
if %B%... substitutes the value of B at the time the block was parsed. AT that time, B was not defined, so nothing is substituted, so the result is if == HOLA (... and cmd does not expect ( at this point and objects.
The complex line I've shown performs a set command on all variables that start b and assigns %%b to the variable name, %%c to its current value. There may be many variables that start B, so we need to select exactly B, and if B's value in %%c is HOLA then set Same, otherwise set Same to nothing, which means that Same will either be assigned, or not assigned respectively.
if defined operates on the current value of the variable, so same can be used safely to do the if processing.
BUT The clean way to do this is by using delayedexpansion.
Stephan's DELAYEDEXPANSION link
I'm trying to lern how to make batch files, and decided to make a simple calculator, and I want it to take user input when it starts but I don't know how to make it work.
What I mean is I write:
calc 25 * 78
and it outputs me the result without asking for the arguments again.
The way I coded it I need the arguments to be seperated aka: each number and operator have thier own variable and the only way I was able to do that so far is if numbers have set length.
Here is my code in case it might help:
#echo off
set /p Action= podaj dzialanie
set number1= %action:~2,1%
set number2= %action:~4,1%
set operation= %action:~0,1%
set /a result= number1 %operation% number2
echo %number% %operation% %number2%=%result%
I'm not sure if I understand how you're intending to use the script, so this is one idea:
#Echo Off
:PoprosDzialanie
For %%G In (Action Integer1 Operator Integer2 Result)Do Set "%%G="
Set /P "Action=podaj dzialanie "
If Not Defined Action GoTo PoprosDzialanie
Call :AnalizujArgumenty %Action%
If ErrorLevel 1 GoTo PoprosDzialanie
Set /A result=Integer1 %Operator% Integer2
If Defined Result (Echo %Integer1% %Operator% %Integer2% = %Result%
"%__AppDir__%timeout.exe" 3 /NoBreak>NUL)
Rem Your script continues between this line and the next none empty line.
GoTo :EOF
:AnalizujArgumenty
If Not "%4"=="" Exit /B 1
If "%3"=="" Exit /B 1
2>NUL Set /A Integer2=%3
If ErrorLevel 1 Exit /B 1
If Not "%2"=="*" If Not "%2"=="/" If Not "%2"=="+" If Not "%2"=="-" Exit /B 1
Set "Operator=%2"
2>NUL Set /A Integer1=%1
If ErrorLevel 1 Exit /B 1
Exit /B
The input is sent to :AnalizujArgumenty where each argument is analysed to ensure that only three arguments were passed and that those arguments were valid, i.e integer operator integer, (where operator can be only *, /, +, or -). If those pass analysis the variables are defined, and your calculation is performed, otherwise they're undefined and the action prompt is repeated.
If you have other actions to perform in your script, those can be added where Remarked, (you may optionally delete the Remark).
It's easy when you rely on correct parameters:
#echo off
set /a "result=%*"
echo Result is %result%
Works with or without spaces:
test.bat 5 + 3
test.bat 3*4
A bit more code, if you want to ask the user when there are no parameters:
#echo off
if "%*"=="" goto :AskUser
set /a "result=%*" || goto :AskUser
echo Result is %result%
goto :eof
:AskUser
REM no or invalid parameter(s)
echo some explanation of what input is expected.
set /p "Action= podaj dzialanie : "
set /a "result=%action%" || goto :AskUser
echo Result is %result%
%* is "all parameters". (%1 is the first parameter, %2 the second, etc. until %9, if you want to check each parameter, but then you lose the advantage to omit the spaces)
I am writing a batch file with a for loop and am having trouble getting the left and mid logic working. I have successfully got this logic working outside of the for loop and have attached the code here.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
REM ZKR
set /a value = 1000
set /a range = "%value%"
set /a secrange = "%value%"
for /l %%x in (1, 1, 10) do (
echo %range%
set range=%range:~0,1%
set secrange=%secrange:~1,3%
echo !range!
echo !secrange!
set "newstr=!range!.!secrange!"
echo !newstr!
PAUSE
)
The output is shown here.
Split 1000
So the above cmd bat is able to split the string and then combine it with a period mark in the middle of the two split strings.
However, in my for loop I don't achieve this result. I know it may be a syntax issue but I am not sure how to fix it (I have tried a lot of different things already).
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
REM ZKR
set /a range = "X"
set /a secrange = "X"
for /l %%x in (1, 1, 10000) do (
if %%x geq 1000 (
if %%x lss 10000 (
set "range=%%x"
set "secrange=%%x"
echo !range!
echo !secrange!
set "range=%range%:~0,1%"
echo !range!
echo %range%
set secrange=%secrange:~1,3%
echo !secrange!
echo %secrange%
set "newstr=!range!.!secrange!"
echo !newstr!
PAUSE
)
REM End x greater 1000
)
REM End for loop
)
I use secrange the same way as I did in the original code without the for loop. I tried to manipulate the range portion with many different syntax combos but have not been able to output the left character 1. I only am able to output 0 for both characters.
I attached the output here. Bad Output
I understand there is an error (Echo is Off). Please ignore that it is just based off my echo output type for my personal testing.
The important two outputs are the third and fourth line (whichever one is referring to the correct syntax). Does anyone know how to fix the syntax in order to get the left character of 1000 in the for loop and the remaining 3 zeros of the 1000?
Im trying to make a batch file that loops thru an array containing numbers like this: 1 2 3 4 5.
In the first itteration of the loop I like to pick token 1 and 2. In the second 2 and 3, in the third 3 and 4 and so on.
I do think I should use ! in the variables first and second that I use as tokens. Like in the first FOR /F, but when I do, I get: !first!" was not expected here.
And if I use %, it does not count up.
Everything works except the variable tokens. Any one knowes how to? Any help or suggestions greatly appriciated.
This is the part Im struggeling with:
setlocal EnableDelayedExpansion
set first=1
set second=2
set N=4
set output="1 2 3 4 5"
set output=%output:"=%
for /L %%a in (1,1,%N%) do (
if !counter! equ active (
set /a first+=1
set /a second+=1
)
FOR /F "tokens=!first!" %%a IN ("%output%") DO (
set nr1=%%a
)
FOR /F "tokens=%second%" %%a IN ("%output%") DO (
set nr2=%%a
)
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set counter=active
)
You cannot use delayed expanded variables in the options string of for /F. Neither can you use other for variables for that. But you can use normally (immediately) expanded variables. Also you can use argument references like %1, for example.
So a nice work-around for your problem is to place the for /F loop in a sub-routine and use call in the main program with the delayed expanded variables as arguments, like this:
#echo off
setlocal EnableDelayedExpansion
set /A first=1
set /A second=2
set /A N=4
set "output=1 2 3 4 5"
set "counter="
for /L %%a in (1,1,%N%) do (
if defined counter (
set /A first+=1
set /A second+=1
)
call :SUB !first! !second!
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set "counter=active"
)
endlocal
exit /B
:SUB val_token1 val_token2
for /F "tokens=%~1,%~2" %%a in ("%output%") do (
if %~1 LSS %~2 (
set "nr1=%%a"
set "nr2=%%b"
) else if %~1 GTR %~2 (
set "nr1=%%b"
set "nr2=%%a"
) else (
set "nr1=%%a"
set "nr2=%%a"
)
)
exit /B
Since you are extracting tokens from the same string, I combined your two for /F loops into a single one. The if block in the for /F loop in the sub-routine :SUB is there just in case the second token number is not always greater than the first one. But if that can guaranteed, the for /F loop needs to contain only set "nr1=%%a" and set "nr2=%%b".
I need the solution to the following problem in batch file,
The user gives the input with a space in between every next input element.
The inputs need to be stored in an array as array elements.
The input has to be taken as the case name and the particular case need to be executed.
Hence every case with the name of the array element need to be executed
The array size is not predetermined. It varies as the user may give any number of inputs
The algorithm needs to be like this,
User input numbers are 1 2 4 6 which are stored in the array a[i]
a[i] = {1,2,4,6}
for i = 1 to len(a[i])
CALL :CASE_%a[i]% # jump to :CASE_1, :CASE_2, etc.
:CASE_1
Echo “am in case1”
:: go to the for loop
:CASE_2
Echo “am in case2”
:: go to the for loop
:CASE_3
Echo “am in case3”
:: go to the for loop
:CASE_4
Echo “am in case4”
:: go to the for loop
:CASE_5
Echo “am in case5”
:: go to the for loop
:CASE_6
Echo “am in case6”
:: go to the for loop
:CASE_7
Echo “am in case7”
:: go to the for loop
:CASE_8
Echo “am in case8”
:: go to the for loop
End for
Case_1, Case_2, Case_4, Case_6 only need to be executed as the input is 1 2 4 6.
Is this possible in batch file?
I will split the problem in three parts. Putting the data into the array, getting the data from the array and simulating a case instruction, not present in batch.
#echo off
setlocal enableextensions enabledelayedexpansion
set /p "data=Input data:"
Let's assume the user inputs only numeric values (to avoid all the necessary check and look only to the problem) as indicated, space separated. Put them into a "array". No, arrays are not present in batch scripts, but can be simulated giving to variables the proper names.
set "arrayLength=0"
for %%e in (%data%) do (
set /a "arrayLength+=1"
set "a[!arrayLength!]=%%e"
)
This iterates over the input data, and for each element, a counter is incremented and a variable named a[+counter value+] is created. No, it is not an array, just a variable with a name to allow us simulate the array sintax. As the counter (arrayLength) is beeing changed inside the loop, delayed expansion is needed to read this changed value, and sintax for variable read changes from %var% to !var!
Now, data is in the "array". We are going to iterate over the "array", checking its contents.
for /l %%i in (1 1 %arrayLength%) do (
echo element %%i is : !a[%%i]!
)
The sintax to read the content of each of the elements requires delayed expansion too. In this case we are not changing the value inside the block, but the name of the readed variable is being generated dinamically in each iteration. Remember, no arrays, just variables with an adecuated name. And we generate the name using the for variable.
How to test for the values of the "array"?
echo Test IF
for /l %%i in (1 1 %arrayLength%) do (
if "!a[%%i]!"=="1" (
echo Case 1
) else if "!a[%%i]!"=="2" (
echo Case 2
) else if "!a[%%i]!"=="3" (
echo Case 3
) else if "!a[%%i]!"=="4" (
echo Case 4
) else if "!a[%%i]!"=="5" (
echo Case 5
) else if "!a[%%i]!"=="6" (
echo Case 6
) else (
echo NO CASE
)
)
This iterates over the "array" and uses a cascaded IF sintax to check for the allowed values. It's easy to code, fast but not the most comfortable to maintain if there are frequent changes to the cases.
An alternative to the IF cascade is the usage of subroutines. Depending on the value of the "array" element, call one label or other. Just name the labels adequately.
Then you can test if the label/subroutine exists and then call it.
Or you can call it directly without previous check and test for errors in the call that will indicate the label does not exist.
Or a table of routines can be defined.
For a sample, if the following labels are defined
:case[1]
echo Case 1
goto :eof
:case[2]
echo Case 2
goto :eof
:case[3]
echo Case 3
goto :eof
:case[4]
echo Case 4
goto :eof
:case[5]
echo Case 5
goto :eof
:case[6]
echo Case 6
goto :eof
the code to call this subroutines with a previous check of existence in batch file (%~f0 is the current batch file with full path) could be
rem Use labels in batch with previous test of existence
for /l %%i in (1 1 %arrayLength%) do (
findstr /l /c:":case[!a[%%i]!]" "%~f0" > nul 2>nul
if not errorlevel 1 (
call :case[!a[%%i]!]
) else (
echo NO CASE
)
)
This uses findstr to read the current batch file searching for the :case[n] label. On no error, the label has been found and the call is made. But having to read the batch file to test for the label existence is not the fastest operation.
To not check for label existence and directly do the call, this code can be used
rem Use labels in batch without check
for /l %%i in (1 1 %arrayLength%) do (
ver>nul
call :case[!a[%%i]!] 2>nul
if errorlevel 1 echo NO CASE
)
The 'ver>nul` line symply ensures errorlevel is cleared before calling the subroutine. The call is made and errorlevel is checked. But there is no way of knowing if the errorlevel comes from the call instruction or from the inside of the called subroutine.
Other option is to have a table of rutines to call for each of the posible values of the elements in the array. So if a table for each of the allowed elements is created, pointing the the labels of the subroutines
set "func[1]=:case[1]"
set "func[2]=:case[2]"
set "func[3]=:case[3]"
set "func[4]=:case[4]"
set "func[5]=:case[5]"
set "func[6]=:case[6]"
The following code can call the adecuated subroutine, cheching if the function for the value is defined
for /l %%i in (1 1 %arrayLength%) do (
for %%v in (!a[%%i]!) do if defined func[%%v] (
call !func[%%v]!
) else (
echo NO CASE
)
)
The inner for is needed to get a reference to the value inside the "array" to be used to access the name of the function inside the func "array".
And ..... anything better anyone could imagine.
After all this, Which code should be used? What works for you. It all depends of the real problem to solve
The IF cascade is fast and easy but for a problem with frequent changing cases, is harder to maintain.
The findstr is time consuming.
The call without check can be a real nigntmare.
The function table is in the middle. Is not as fast as having all the code in the block as in the IF solution, but is faster than the findstr while checking for subroutine definition. It is not hard to code, but for small problems is innecesary.
For simple cases, IF constructs are all you need. For complex problems, if the code is long and cases needs to be changed, added, ... the function table makes things easier.
So, it can result in
#echo off
setlocal enableextensions enabledelayedexpansion
set /p "data=Input data:"
set "arrayLength=0"
for %%e in (%data%) do (
set /a "arrayLength+=1"
set "a[!arrayLength!]=%%e"
)
rem func[ allowedValue ] = label to call
set "func[1]=:handleCase_1"
set "func[2]=:handleCase_2"
set "func[3]=:handleCase_3"
set "func[4]=:handleCase_4"
set "func[5]=:handleCase_5"
set "func[6]=:handleCase_6"
for /l %%i in (1 1 %arrayLength%) do (
for %%v in (!a[%%i]!) do if defined func[%%v] (
call !func[%%v]!
) else (
echo NO CASE
)
)
for /l %%i in (1 1 %arrayLength%) do (
if "!a[%%i]!"=="1" (
echo Case 1
rem Or, we can
rem call :handleCase_1
) else if "!a[%%i]!"=="2" (
echo Case 2
) else if "!a[%%i]!"=="3" (
echo Case 3
) else if "!a[%%i]!"=="4" (
echo Case 4
) else if "!a[%%i]!"=="5" (
echo Case 5
) else if "!a[%%i]!"=="6" (
echo Case 6
) else (
echo NO CASE
)
)
endlocal
exit /b
:handleCase_1
echo Case 1
goto :eof
:handleCase_2
echo Case 2
goto :eof
:handleCase_3
echo Case 3
goto :eof
:handleCase_4
echo Case 4
goto :eof
:handleCase_5
echo Case 5
goto :eof
:handleCase_6
echo Case 6
goto :eof
Windows batch does not have true support for arrays, but they can be emulated via clever use of variable names. For example, a[1] and a.len are ordinary variable names, the script does not know of any object named a. I tend to use dot notation instead: a.1, a.2, ... a.len. You are free to develop your own style.
#echo off
setlocal enableDelayedExpansion
set a[1]=1
set a[2]=2
set a[3]=4
set a[4]=6
set a.len=4
:: You can also do multiple numerical assignments on one line using SET /A
:: set /a "a[1]=1, a[2]=2, a[3]=4, a[4]=6, a.len=4"
for /l %%i in (1 1 %a.len%) do (
call :CASE_!a[%%i]!
)
:: You must exit the script so you don't fall into the subroutines after the loop finishes
exit /b
:CASE_1
Echo am in case1
exit /b
:CASE_2
Echo am in case2
exit /b
:CASE_3
Echo am in case3
exit /b
:CASE_4
Echo am in case4
exit /b
:CASE_5
Echo am in case5
exit /b
:CASE_6
Echo am in case6
exit /b
:CASE_7
Echo am in case7
exit /b
:CASE_8
Echo am in case8
exit /b
I reviewed your problem and, in my opinion, it does not require the use of an array. I think you are talking about a list. This way, the stated problem may be solved using a list this way:
#echo off
rem The user gives the input with a space in between every next input element.
set /P input=
rem The input has to be taken as the case name and the particular case need to be executed.
rem Hence every case with the name of the array element need to be executed.
for %%i in (%input%) do call :CASE_%%i
goto :EOF
:CASE_1
Echo am in case1
exit /B
:CASE_2
Echo am in case2
exit /B
:CASE_3
Echo am in case3
exit /B
:CASE_4
Echo am in case4
exit /B
:CASE_5
Echo am in case5
exit /B
:CASE_6
Echo am in case6
exit /B
For example:
C:\> test
1 2 4 6
am in case1
am in case2
am in case4
am in case6
Any solution that is forced to use an unnecessary array would be more complicated...