I have tried multiple things with a code like this.
#echo off
set %1%=A
set %2%=B
set %3%=C
set %4%=D
set %5%=E
set %6%=F
set %7%=G
set %8%=H
echo %1%%2%%3%%4%%5%%6%%7%%8%%9%
But kinda nothing worked, the output was this:
1%2%3%4%5%6%7%8
How do I get it to output ABCDEFGH?
Try with
#echo off
set _1=A
set _2=B
set _3=C
set _4=D
set _5=E
set _6=F
set _7=G
set _8=H
echo %_1%%_2%%_3%%_4%%_5%%_6%%_7%%_8%
Starting from the concept, your problem is that %n with n in the range 0..9 is handled by the batch parser as an command line argument to the batch file, not a variable expansion operation.
You can use number prefixed variable names, but then you will require to enable delayed expansion and change the variable expansion syntax from %varName% in to !varName! to be able to retrieve the value. It is easier not use number prefixed variables names.
The second problem is that the syntax %varName% is only used where the variable value needs to be retrieved. When you set the value, the syntax is set varName=varValue, or still better you can quote the operation as set "varName=varValue" to avoid problems with special characters and inclusion of unneeded ending spaces.
Your question is not clear. The code below do exactly what you requested:
#echo off
set A=A
set B=B
set C=C
set D=D
set E=E
set F=F
set G=G
set H=H
echo %A%%B%%C%%D%%E%%F%%G%%H%
However, is likely that this obvious solution is not what you are looking for...
If you want to know if is there a way to "automatically" define a series of variables and process they all, then the solution is to use an array. You may read the description of the array concept in this Wikipedia article and a detailed explanation of array management in Batch files at this answer. For example:
#echo off
setlocal EnableDelayedExpansion
rem Create "a" array with all elements given:
set n=0
for %%a in (A B C D E F G H) do (
set /A n=n+1
set a[!n!]=%%a
)
rem Show the 8 elements of "a" array
echo %a[1]%%a[2]%%a[3]%%a[4]%%a[5]%%a[6]%%a[7]%%a[8]%
rem Join *all* the elements of "a" array in a single variable
set "all="
for /L %%i in (1,1,%n%) do set "all=!all!!a[%%i]!"
echo %all%
Note that the last example works correctly no matters how many elements have been defined in "a" array.
Although you may also write the array elements in a shorter way, ommiting the braquets: set "a1=A" & set "a2=B", etc, and then use echo %a1%%a2%..., you should remember that the use of braquets is a standard notation used in many other programming languages, so it is convenient to keep it.
In one of my batch scripts I need to calculate the duration of an interval in a video file. First the user is asked to input the start and end times:
set /p StartPosition=Start position (HH:MM:SS):
set /p EndPosition=End position (HH:MM:SS):
Then, I would like the batch script to calculate the duration in between.
How can I subtract %StartPosition% from %EndPosition% like this, for example:
00:10:40 - 00:10:30 = 00:00:10
The reason why I can't figure out how to do this is because these numbers are separated by colons.
Edit: This question is different to this question because I do not need the scrip to treat the numbers as time values.
#echo off
setlocal
set /p "StartPosition=Start position (HH:MM:SS): "
set /p "EndPosition=End position (HH:MM:SS): "
set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
EDIT: Some explanations added
This program use the usual method to convert a time in HH:MM:SS format into a number of seconds via the standard formula: seconds = (HH*60+MM)*60+SS. However, the set /A command consider the numbers that start with 0 as written in octal base, and hence 08 and 09 would be invalid octal numbers. To avoid this problem, a digit 1 is placed before expand the number and a 100 is subtracted after, so if HH=08 then 1%HH%-100 correctly gives 8; that is:
set /A seconds = ((1%HH%-100)*60+1%MM%-100)*60+1%SS%-100
There are several methods to split a time given in HH:MM:SS format into its three parts. For example, if we take set EndPosition=HH:MM:SS as base, then we may use a for /F command this way:
for /F "tokens=1-3 delims=:" %%a in ("%EndPosition%") do (
set /A "seconds=((1%%a-100)*60+1%%b-100)*60+1%%c-100"
)
In this program a different method is used. If we match the original EndPosition=HH:MM:SS string with the desired formula, we may construct this mapping scheme:
HH : MM : SS
((1 HH -100)*60+1 MM -100)*60+1 SS -100
In other words: if we replace the colons of the original string by -100)*60+1 and insert ((1 at beginning and -100 at end, we obtain the desired formula; that is:
set /A "seconds=((1%EndPosition::=-100)*60+1%-100"
This is a very efficient method that even allows to replace both EndPosition and StartPosition strings in the same formula (enclosing both parts in parentheses) and directly subtract them:
set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
You may cancel the #echo off command and run the program to review the exact formula that is evaluated after the values of the variables are replaced. For example, when StartPosition=00:10:30 and EndPosition=00:10:40, this is the expression that is evaluated:
set /A "ss=(((100-100)*60+110-100)*60+140-100)-(((100-100)*60+110-100)*60+130-100)"
Just to complete this description, this is the "standard" way to evaluate the same formula using a for /F command:
for /F "tokens=1-6 delims=:" %%a in ("%EndPosition%:%StartPosition%") do (
set /A "ss=(((1%%a-100)*60+1%%b-100)*60+1%%c-100)-(((1%%d-100)*60+1%%e-100)*60+1%%f-100)"
)
The opposite conversion from number of seconds to HH:MM:SS parts is straightforward:
HH=SS/3600, rest=SS%3600, MM=rest/60, SS=rest%60
However, each part in the result must be displayed with two digits, but this formatting may be achieved in a very simple way. Instead of insert three if commands that check if each part is less than 10 and insert a padding zero in such a case, the number 100 is just added to the parts (converting an 8 into 108, for example), and when each part is displayed the first digit is omitted (so just 08 is shown). This is a very efficient method to format numbers that may be performed in the same set /A command used to obtain the parts. For example:
set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
In this way, the conversion of two times into two number of seconds, their subtraction and the opposite conversion and formatting to HH:MM:SS is performed in two SET /A commands, that even may be written in a single, long line.
Output examples:
Start position (HH:MM:SS): 00:10:30
End position (HH:MM:SS): 00:10:40
Duration=00:00:10
Start position (HH:MM:SS): 00:10:45
End position (HH:MM:SS): 00:11:05
Duration=00:00:20
This is possible to do in pure batch by parsing each field as an independent string, then doing arithmetic on them. Many practical solutions call into some other program to do the date math.
The following code calls into PowerShell to use the .NET DateTime class to do the parsing for you.
C:\> set "StartPosition=00:10:30"
C:\> set "EndPosition=00:10:40"
C:\> PowerShell.exe -c "$span=([datetime]'%EndPosition%' - [datetime]'%StartPosition%'); '{0:00}:{1:00}:{2:00}' -f $span.Hours, $span.Minutes, $span.Seconds"
00:00:10
This executes two lines of PowerShell code; one to convert both times into DateTime objects and subtract them, and the other to output the result in the format you specified.
Here's a working prototype:
#echo off
set T1=00:10:45
set T2=00:11:05
set HOUR1=%T1:~,2%
set MIN1=%T1:~3,-3%
set SEC1=%T1:~-2%
set HOUR2=%T2:~,2%
set MIN2=%T2:~3,-3%
set SEC2=%T2:~-2%
set /A TOTAL_SEC1=%HOUR1%*3600+%MIN1%*60+SEC1
set /A TOTAL_SEC2=%HOUR2%*3600+%MIN2%*60+SEC2
set /A DIFF=%TOTAL_SEC2%-%TOTAL_SEC1%
echo %DIFF%
Output:
20
Its not complete, but its a reasonable start.
I think, #Aacini has cleared Everything here. He got you, Before I Do. But, I want to Improved on him as - by using For Loop to make code Easier.
Note: Everything after 'REM' is a Comment for the sake of understanding easily...
All You need to DO is Copy It into Your Batch File. And, Use it as follows (in your main code):
Syntax: Call :Time [Your Time 1] [Operation] [Your Time 2]
And, You can Now apply - any operation - including 'Addition, Substraction, Division, Multiplication' ;)
The Time Function
--------------Copy the Below Code----------
:Time [Start_Time] [Operation] [End_Time]
SetLocal EnableDelayedExpansion
REM Creating a portable Function for your Job. :)
REM Reading Start-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~1") Do (
Set _Start_Hour=%%A
Set _Start_Min=%%B
Set _Start_Sec=%%C
)
REM Reading End-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~3") Do (
Set _End_Hour=%%A
Set _End_Min=%%B
Set _End_Sec=%%C
)
REM Removing leading Zero's - if any... 'CMD assumes it as octal - otherwise'
For %%A In (Hour Min Sec) Do (
For %%B In (Start End) Do (
IF /I "!_%%B_%%A:~0,1!" == "0" (Set _%%B_%%A=!_%%B_%%A:~1!)
)
)
REM Applying Operation on the given times.
For %%A In (Hour Min Sec) Do (Set /A _Final_%%A=!_Start_%%A! %~2 !_End_%%A!)
REM Handling a little Exceptional errors! - due to the nature of time (60 sec for a min.)
SET _Extra_Hour=0
SET _Extra_Min=0
REM Two Cases can arise in each part of time...
:Sec_loop
IF %_Final_Sec% GTR 59 (Set /A _Extra_Min+=1 & Set /A _Final_Sec-=60 & Goto :Sec_loop)
IF %_Final_Sec% LSS 0 (Set /A _Extra_Min-=1 & Set /A _Final_Sec+=60 & Goto :Sec_loop)
Set /A _Final_Min+=%_Extra_Min%
:Min_loop
IF %_Final_Min% GTR 59 (Set /A _Extra_Hour+=1 & Set /A _Final_Min-=60 & Goto :Min_loop)
IF %_Final_Min% LSS 0 (Set /A _Extra_Hour-=1 & Set /A _Final_Min+=60 & Goto :Min_loop)
Set /A _Final_Hour+=%_Extra_Hour%
REM Saving Everything into a Single Variable - string.
Set _Final_Time=%_Final_Hour%:%_Final_Min%:%_Final_Sec%
REM Displaying it on the console. ;)
Echo.%_Final_Time%
Goto :EOF
--------------End OF Code----------------------
You can Also visit, my Website - based on Batch Programming. (www.thebateam.org) You'll find alot of stuff there - to help you out. :)
Here's the Final Output - When I saved the Code in Answer.bat File
To offer a concise alternative to Ryan Bemrose's helpful, PowerShell-based answer:
:: Sample variable values.
set "StartPosition=00:10:30"
set "EndPosition=00:10:40"
:: Use PowerShell to perform the calculation,
:: using the .NET System.Timespan ([timespan]) type.
powershell -c \"$([timespan] '%EndPosition%' - '%StartPosition%')\"
Yes, you pay a performance penalty for invoking the PowerShell CLI, but I invite you to compare this solution to Aacini's clever, but highly obscure batch-language-only solution in terms of readability and conceptual complexity.
Generally speaking:
cmd.exe is a shell and, historically, shells have provided very limited language capabilities themselves, as their focus was on calling built-in or external commands.
cmd.exe's language, as used in batch files (.cmd, .bat) is very limited, and saddled with many counterintuitive behaviors that can't be fixed so as not to break backward compatibility.
Over the decades, users have learned to stretch the language to its limits, coming up with many clever techniques to squeeze more functionality out of it. While helpful if you're stuck on pre-PowerShell systems (virtually extinct at this point) or you must use batch files and performance is paramount (rarely the case), the obscurity of these techniques makes them both hard to understand and to remember.
cmd.exe's successor, PowerShell, with its .ps1 scripts, offers a far superior language that offers virtually unlimited access to .NET functionality and COM.
PowerShell too has its fair share of counterintuitive behaviors that can't be fixed, but, by and large, it is a far more capable and predictable language than the batch language; some of its undeniable, but unavoidable complexity comes from having to talk to multiple worlds (.NET, COM, WMI) while still also acting as a shell (with respect to calling external programs and the shell-like syntax of its built-in command as well as user-defined ones).
Here, the batch file uses a call an external program, powershell.exe, the PowerShell CLI, to delegate the task at hand to its superior language.
Calling the PowerShell CLI is expensive in terms of performance, but offers a way to perform tasks that batch files either cannot, or can only do with much more effort and/or highly obscure techniques.
Of course, needing to "speak" both the batch language and PowerShell to implement a given task adds complexity of its own, so the logical progression is to implement the entire task in PowerShell (in a .ps1 script).
Unfortunately, PowerShell puts up some road blocks here, in the name of security:
In workstation editions of Windows, execution of scripts is disabled by default, and requires a one-time call such as the following to enable it (see this answer for background information):
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
.ps1 scripts - unlike batch files - cannot be executed directly from outside PowerShell, notably not by double-clicking in File Explorer or from a cmd.exe session.
From inside a PowerShell session that is not a concern, but if you do need this capability in a given scenario, a simple workaround is to create a companion batch file with the same base file name as the .ps1 script (e.g., foo.cmd in the same directory as the target PowerShell script, foo.ps1, with the following, generic content:
#powershell -noprofile -file "%~dpn0.ps1"
I have a very long string for use as a map. It is about 50 characters (10 for my example though). I also have a string that I want to use as a number that represents the players position on the map string:
#ECHO OFF
SET map=CGWGWBBBTB
SET playerposition=1
So if playerposition was 3 I would want to receive W from this next line of code:
%map:~%playerposition%,1%
It seems I can not retrieve the playereposition variable like that.
#ECHO OFF
SETLOCAL
#ECHO OFF
SET map=CGWGWBBBTB
SET playerposition=1
CALL SET playereposition=%%map:~%playerposition%,1%%
SET play
SET playerposition=2
CALL SET playereposition=%%map:~%playerposition%,1%%
SET play
SET playerposition=3
CALL SET playereposition=%%map:~%playerposition%,1%%
SET play
You could also use a subroutine:
SET playerposition=4
CALL :setsubstr playereposition map %playerposition% 1
SET play
GOTO :EOF
:setsubstr
SETLOCAL ENABLEDELAYEDEXPANSION
SET "return=!%2:~%3,%4!"
endlocal&SET "%1=%return%"
GOTO :EOF
Here, the temporary variable return is used to contain the substring-from-3rd-parameter-length-4th-parameter-of the string second-parameter into variable-name first-parameter.
The endlocal&... disposes of the temporary variable and uses a parsing trick to assign the value to %1.
Note that using this approach also allows the routine to be made "smart" by allowing (with changes) an omitted 4th parameter to default to 1, for instance.
Note also in all of this that position-counting in a string starts from 0, not 1.
I am working on a script to get max lengths of each column, I'm trying to store lengths of max length in _c1...n vars. number of columns unknown.
I was able to get length for each column, create variables to store each with set _c!i! = !n!, n is the length
but in order to set the max length for a particular column I need to compare current with max and use something like !_c!!i!! which doesn't work, any ideas how to refer a variable which part of it's name coming from another variable?
Thanks...
I assume that you are using the delayed expansion character because you are working inside a set of brackets "()". Doing that makes your process harder. I know that method is easier to read, but it is harder to code for.
Inside brackets, I know of only one method to access a variable that was 'built' out of one or more variables. That is to use the call function to cause the assembled variable to 'activate'. This method works both inside and outside of brackets.
Here is a small example:
#echo off
setlocal enabledelayedexpansion
(
set i=10
set _c!i!=something
:: below is equivalent to echo !_c10!
call echo %%_c!i!%%
)
endlocal
Output:
something
You can do almost everything using a CALL in front of it that you can without it, though in XP or earlier you cannot call internal commands like if and can only call 'external' programs like FIND.EXE.
If you can work outside of a set of brackets by possibly using a call :label statement, you can simply access the variable like this:
#echo off
setlocal enabledelayedexpansion
set i=10
set _c!i!=something
:: The below 2 statements are equivalent to `echo %_c10%`
echo !_c%i%!
call echo %%_c!i!%%
endlocal
Output:
something
something
The CALL technique suggested by James K will work, but it is relatively slow and can be unsafe, depending on the content of the variable.
The following looks more complicated, but it is significantly faster and more reliable:
for %%A in (!i!) do echo !_c%%A!
In your case there could be a third solution be possible, if your variables contains only numbers.
#echo off
setlocal enabledelayedexpansion
(
set i=10
set _c!i!=4711
set /a tmp=_c!i!
echo !tmp!
)
This works, as SET /A can access the content of a variable without the nedd of explicitly expansion charaters.
Hi Can anyone help me out in this problem.
I need to create multiple file?? i give with any example. In some folder, say Folder Records.
"Record" folder contain 1 file by name "example2tought1023.au" . i need to generate same file contains, multiple time just by increasing the numbers.
i should get result like this example2tought1023.au example3tought1024.au example4tought1025.au example5tought1026.au
This is what I currently have:
SET count=9
SET filename_1=example
SET filename_2=thought
SET extension=.au
SET start_1=2
SET start_2=1023
SET source=%filename_1%%start_1%%filename_2%%start_2%%extension%
FOR /L %%i IN (1, 1, %count%) DO (
REM These two lines do not work!
SET /a n=%start_1%+%%i
SET /a number_2=%start_2% + %%i
SET destination=%filename_1%%number_1%%filename_2%%number_2%%extension%
ECHO %destination%
REM COPY %source% %destination%
)
PAUSE
but the lines in the FOR /L loop do not work
You have mis-identified which lines are not working :-)
The problem you are having relates to when variables are expanded. Normal expansion using percents occurs when the line is parsed, and your entire FOR statement, including the parenthesised DO clause, is parsed in one go. So the following line
SET destination=%filename_1%%number_1%%filename_2%%number_2%%extension%
is seeing the values of %number_1% and %number_2% that existed before the loop was executed. Obviously not what you want. The solution is simple - you need to use delayed expansion (the value at run time instead of parse time). You do that by 1st enabling delayed expansion using setlocal enableDelayedExpansion, and then use !number_1! instead of %number_1%.
You are not consistent with your variable names (n vs number_1).
I think you want to count from 0 to count-1 instead of from 1 to count.
You do not have to explictly expand the variable when using a variable in a SET /A expression. You can simply use the variable name without percents or exclamations. But this only works with the SET /A command.
You can also perform multiple computations and assignments with a single SET /A command by using a comma between each assignment.
#echo off
setlocal enableDelayedExpansion
REM counts from 0 to count, so 8 = 9 copies
set count=8
set filename_1=example
set filename_2=thought
set extension=.au
set start_1=2
set start_2=1023
set source=%filename_1%%start_1%%filename_2%%start_2%%extension%
for /L %%i in (0, 1, %count%) do (
set /a "number_1=start_1+%%i, number_2=start_2+%%i"
set destination=%filename_1%!number_1!%filename_2%!number_2!%extension%
echo !destination!
REM copy %source% !destination!
)
pause