%x:~12,3% Returns 3 characters starting at the 12:th character in x variable.
What I have been trying to accomplish is using variables instead of 12 and 3.
Let's say y=12 and z=3.
Then, you can't use %x:~%y%,%z%%, because CMD will think %x:~% is a variable.
What you can do is set var=%%x:~%y%,%z%%%. This will expand the inside variables y and z, but not x, so that the value of var is %x:~12,3%.
The remaining task at hand now is to finally expand %x:~12,3%. I have been trying to append echo in the beginning so that var=echo %x:~12,3%.
If at the commandline or in a batch file you now use %var%, this should execute the echo command, and expand the succeeding expression, but it doesnt, instead echo %x:~12,3% results in simply %x:~12,3% being printed to the screen, unexpanded.
I was thinking that maybe if you set var to %x:~12,3%, then echo it
and pipe the output into another ECHO command or SET command that the expression would be expanded, but it seems that ECHOand SETdoesn't accept data being piped into it at all?
How can I make this work?
I copied the entire text below from this answer; I just changed the names of variables and particular examples to match the ones of this question:
%x:~12,3% returns 3 characters starting at the 12:th character in x
variable. What I have been trying to accomplish is using variables
instead of 12 and 3. Let's say y=12 and z=3.
If you want to use another variables for substring position and lenght, then you must know that the replacement of variables enclosed in percents by their values is parsed from left to right; this mean that: %x:~%y%,%z%% don't give the desired result because it mean: show the value of x:~ variable, followed by y, followed by the value of , variable, etc.
To solve this problem you must use Delayed Expansion, that is, insert setlocal EnableDelayedExpansion command at beginning, enclose substring variables in percents, and enclose the original variable in exclamation marks:
setlocal EnableDelayedExpansion
set x=0123456789ABCDEF
set y=12
set z=3
set var=!x:~%y%,%z%!
You may also use parameters of FOR commands as indexes: for /F "tokens=1,2" %%i in ("%y% %z%") do set var=!x:~%%i,%%j!.
To get the value of a substring when the index change inside FOR/IF enclose the variable in double percents and precede the command with call. For example, to show a substring at a random y position between 0 and 12 and lenght z:
if %some% == %test% (
set /A y=!random! %% 13
call echo %%x:~!y!,%z%%%
)
You may also use this method outside parentheses in order to avoid the Delayed Expansion:
call echo %%x:~%y%,%z%%%
Another way to achieve previous process is using an additional FOR command to change the delayed expansion of the index by an equivalent replaceable parameter, and then use the delayed expansion for the original variable. This method run faster than previous CALL:
if %some% == %test% (
set /A y=!random! %% 13
for %%y in (!y!) do echo !x:~%%y,%z%!
)
You need to enable delayed expansion.
#echo off
setlocal enabledelayedexpansion
set string=1234567890abcdef
set substring_start=12
set substring_length=3
set substring=!string:~%substring_start%, %substring_length%!
set command=echo !substring!
!command!
pause
#ECHO OFF
SETLOCAL
SET "x=abcdefghijklmmopqrstuvwxyz"
SET /a start=12
SET /a length=3
CALL SET "var=%%x:~%start%,%length%%%"
ECHO var=%var%
CALL echo %%x:~%start%,%length%%%
SETLOCAL ENABLEDELAYEDEXPANSION
SET /a start=6
SET /a length=4
SET "var=!x:~%start%,%length%!"
ECHO var=%var%
echo !x:~%start%,%length%!
GOTO :EOF
Two methods - the first in standard mode and the second using delayedexpansion. There are hundreds of examples on SO about delayedexpansion.
Related
I've mentioned constant index in the title because all question in StackOverflow related to array indexing in a batch file were focused on accessing array using variable index inside a loop.
I'm new to batch scripting. I want to print array value with a constant index if the array is initialized as a list(in one line) rather than each element being initialized individually. I've written a snippet in which I can print the value of arr but not list.
#echo off
set arr[0]=1
set arr[1]=2
set arr[2]=3
set list=1 2 3 4
REM Result is 2
echo %arr[1]%
REM Won't print
echo %list[1]%
A list in a string isn't an array, this answer 2 days ago shows how to turn a string list into an array.
To have the array index zero based use this changed version
:: SO_51225079.cmd
#echo off & Setlocal EnableDelayedExpansion
set arr[0]=1
set arr[1]=2
set arr[2]=3
set i=-1&set "list= 1 2 3 4"
Set "list=%list: ="&Set /a i+=1&Set "list[!i!]=%"
set list
REM Result is 2
echo %arr[1]%
REM Won't print
echo %list[1]%
Sample output:
> SO_51225079.cmd
list[0]=1
list[1]=2
list[2]=3
list[3]=4
2
2
You might be confusing Batch and PowerShell. In PowerShell, yes, you can initialize an array on one line:
$list = 1, 2, 3, 4
$list[1]
# output here would be 2
In the Batch scripting language, there are no array objects. You can simulate arrays by having similarly or sequentially named scalar variables, but the Batch language doesn't provide methods such as split() or splice() or push() or similar.
Often in Batch, splitting a string on spaces (or commas or semicolons) is accomplished by tokenizing using a for loop.
#echo off & setlocal
rem // Quoting "varname=val" is the safest way to set a scalar variable
set "list=1 2 3 4"
rem // When performing arithmetic using "set /a", spacing is more flexible.
set /a ubound = -1
rem // Split %list% by tokenizing using a for loop
for %%I in (%list%) do (
set /a ubound += 1
rem // Use "call set... %%ubound%%" to avoid evaluating %ubound% prematurely.
rem // Otherwise, %ubound% is expanded when the for loop is reached and keeps the
rem // same value on every loop iteration.
call set "arr[%%ubound%%]=%%~I"
)
rem // output results
set arr[
setlocal enabledelayedexpansion
rem // You can also loop from 0..%ubound% using for /L
for /L %%I in (0, 1, %ubound%) do echo Element %%I: !arr[%%I]!
endlocal
As a side note, in that code block I demonstrated two methods of delaying expansion of variables in Batch -- using call set and setlocal enabledelayedexpansion using exclamation marks where delayed retrieval is desired. There are times when it's useful to know both. The enabledelayedexpansion method is usually more readable / more easily maintained, but in some circumstances can clobber values where exclamation marks possibly exist (such as file names). For that reason, I try to avoid enabling delayed expansion for the entire script.
LotPings' answer is clever, but limited in application. The way it works is, using substring substitution it sets and evaluates the value of list to a string of commands (separated by &). Unfortunately, it destroys the value of %list% in the process, and it cannot handle values containing spaces or exclamation marks.
#echo off & setlocal
set "list="The quick brown" "fox jumps over" "the lazy dog!!!""
set /a ubound = -1
for %%I in (%list%) do (
set /a ubound += 1
call set "arr[%%ubound%%]=%%~I"
)
rem // output results
set arr[
The for method of splitting will correctly maintain quoted spaces.
Batch has only one type of variable: string.
An array and a list are very different things (and it is discussed, if those even exist in batch, but they can be emulated). In batch, a list isn't different elements, but just a single string, and an array isn't a single structure, but different independend variables.
Nevertheless, you can split a string (that looks like a list) by delimiters (space is a default delimter) with a for loop:
set list=a b c d
set element=2
for /f "tokens=%element%" %%a in ("%list%") do echo %%a
(Note: this is batch syntax. For use directly on command line, replace each %%a with %a)
Can someone give me an example of where a batch script would act differently with or without delayed expansion? Are there any situations where you would NOT want to use delayed expansion? Thanks.
Look at the following examples...
Example 1: The following code DOESN'T use delayed expansion, so the variables in the for loop are expanded only one time. This means that %Count% will always expand to 0 in each iteration of the loop, no matter what we do to it with the set command:
#echo off
set COUNT=0
for %%v in (1 2 3 4) do (
set /A COUNT=%COUNT% + 1
echo Count = %COUNT%
)
pause
So this script will output:
Count = 0
Count = 0
Count = 0
Count = 0
This is not how this loop is supposed to work.
Example 2: On the other hand, if we use delayed expansion, we have the following script, which will run as expected.
setlocal ENABLEDELAYEDEXPANSION
set COUNT=0
for %%v in (1 2 3 4) do (
set /A COUNT=!COUNT! + 1
echo Count = !COUNT!
)
pause
and, as expected, it will output:
Count = 1
Count = 2
Count = 3
Count = 4
When you use the ENABLEDELAYEDEXPANSION, and expand a variable using ! instead of %, the variable is re-expanded each time, and everything works as it's supposed to.
I wanted to add a great example on how "EnableDelayedExpansion" (EDE) can be useful outside of the ubiquitous FOR loop examples.
Here is a line of earthquake data that I wish to parse (I call it it 1line.txt)
ak_11574812 2015.04.29.193822 62.9525 -148.8849 1.0 9.5 1 49km S of Cantwell, Alaska
The problem I ran into was that last segment of this line does not always start at the same column number. So I needed to create a flexible SET command that will accurately pluck out the last segment of this line.
ECHO OFF
setlocal enableDelayedExpansion
set where=72
set /p line=<1line.txt
set locate=!line:~%where%,28!
echo %locate%
EDE allows me to place a variable (where) inside another variable (line). EDE will translate the variable bracketed by % first, then process the variable bracketed by ! and (in this case) push out the results into the "locate" variable.
Max's answer gives an example of where a batch script would act differently with or without delayed expansion.
For the sake of completeness, let's answer another part of the question and show a situation where you would NOT want to use delayed expansion when your data contain an exclamation mark ! (and show two ways of processing such data):
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_auxFile=%temp%\%~n0.txt"
rem create multiline sample file
>"%_auxFile%" ( for /L %%G in (1,1,3) do echo line %%G is 100%% valid! Sure! Hurrah!)
rem create one-line sample file
>"%_auxFile%" echo this line is 100%% valid! Sure! Hurrah!
echo(
echo --- file content
type "%_auxFile%"
echo(
SETLOCAL EnableDelayedExpansion
echo --- enabled delayed expansion chokes down unescaped exclamation marks^^^! "^!"
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%~G"
echo loop var=%%~G
echo _auxLine=!_auxLine!
)
ENDLOCAL
echo(
SETLOCAL DisableDelayedExpansion
echo --- toggled delayed expansion works although might be laborious!
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%G"
echo loop var=%%G
SETLOCAL EnableDelayedExpansion
echo _auxLine=!_auxLine!
ENDLOCAL
)
ENDLOCAL
echo(
SETLOCAL DisableDelayedExpansion
echo --- keep delayed expansion DISABLED: use CALL command!
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%G"
echo loop var=%%G
call :ProcessVar
)
ENDLOCAL
rem delete the sample file
del "%_auxFile%"
ENDLOCAL
goto :eof
:ProcessVar
echo _auxLine=%_auxLine%
echo WARNING: neither !_auxLine! nor %%G loop variable is available here!
goto :eof
Note that above script shows proper ways of escaping
% percent sign by %% doubling it (delayed expansion does not matter), and
! exclamation mark if delayed expansion is enabled:
"^!" if enclosed in a pair of double quotes, then use the cmd and batch-script general escape character ^ caret;
^^^! otherwise, use three ^ carets.
Output:
==> D:\bat\SO\10558316.bat
--- file content
this line is 100% valid! Sure! Hurrah!
--- enabled delayed expansion chokes down unescaped exclamation marks! "!"
loop var=this line is 100% valid Hurrah
_auxLine=this line is 100% valid Hurrah
--- toggled delayed expansion works although might be laborious!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
--- keep delayed expansion DISABLED: use CALL command!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
WARNING: !_auxLine! as well as %G loop variables are not available here!
==>
As pointed in the answer the main usage of the delayed expansion is the setting and accessing variables in brackets context.
Though it can be useful in another situations too.
Parametrizing substring and string substitution:
#echo off
setlocal enableDelayedExpansion
set "string=test string value"
set start=5
set get_next=6
echo #!string:~%start%,%get_next%!#
set "search_for=string"
set "replace_with=text"
echo #!string:%search_for%=%replace_with%!#
the output will be:
#string#
#test text value#
though this can be achieved with additional call this way is more performant
Using shift command within brackets parameterized argument access
#echo off
echo first attempt:
(
echo "%~1"
shift
echo "%~1"
)
::now the shift command will take effect
setlocal enableDelayedExpansion
echo second attempt:
(
set /a argument=1
call echo %%!argument!
shift
call echo %%!argument!
)
the output will be:
first attempt:
"first argument"
"first argument"
second attempt:
"second argument"
"third argument"
As you can see parameterized argument access can be done only with delayed expansion.
Using for tokens (or function arguments) for parameterization
One more approach for mixing !s and %s this could be useful for nested loops:
#echo off
setlocal enabledelayedexpansion
set begin=2
set end=2
set string=12345
for /f "tokens=1,2" %%A in ("!begin! !end!") do set "string2=!string:~%%A,%%B!"
echo !string2!
endlocal
as you can see now the for command tokens are used as parameters.
Several answers here answer the "How to use delayed expansion?" question or what happen if you don't use delayed expansion. However, the second question is "Are there any situations where you would NOT want to use delayed expansion?" and a couple answers take this question as "how to avoid the problems caused by using delayed expansion?"
My answer answers the question as I understand it: "In which situations is better to NOT use delayed expansion (instead of use it)?"
If you want to exchange the contents of two variables, the simplest way to perform this is using the %standard% variable expansion:
set "var1=%var2%" & set "var2=%var1%"
The way that the %standard% expansion works makes possible to achieve this replacemenmt without using any auxiliary variable. As far as I know, the Batch-file "programming language" is the only one that allows to perform this exchange in this way, that is, making good use of a language "feature" (not via a specialized "exchange" instruction/statement).
I know the question is a little confusing.
This is my script. (easy version)
I've set up those variables
p1 = 1
p2 = 0
p3 = 1
and it goes on and on.
I want to show a list of all of those P values. Then,
I setup a variable "i"
set /a i = 0
:loop
set /a i = %i% + 1
echo P%i% = %p%i%%
goto loop
but then I realized that %p%i%% is not working...
It won't go to %p1% and show me "1",
it just shows me %p1%. and in my full script, it shows me something crazy... SO CAN ANYONE HELP ME OUT HERE?? THANKS A LOT!!
Two simple ways.
The first is to use delayed expansion.
First expand i with %i% and later the expression !p1!
Or use CALL to parse a line a second time.
The line call echo P%i% = %%p%i%%% after the first parse looks like
call echo P1 = %p1%
The second parse will expand %p1% to the content.
setlocal EnableDelayedExpansion
set p1=1
set p2=0
set p3=1
set /a i = 0
:loop
set /a i = %i% + 1
echo P%i% = !p%i%!
call echo P%i% = %%p%i%%%
if %i% == 3 exit /b
goto loop
Or another solution using a for loop
for /L %%i in (1 1 3) do (
echo P%%i = !p%%i!
)
I offer two more solutions in addition to the solutions in answer of jeb not using delayed expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "MyP1=1"
set "MyP2=0"
set "MyP3=5"
set "Count=0"
for /F "tokens=1* delims==" %%I in ('set MyP 2^>nul') do echo Variable %%I has value %%J & set /A Count+=1
echo Variables count is: %Count%
echo/
set "Index=1"
:Loop
if not defined MyP%Index% goto ExitLoop
set /A Value=MyP%Index%
echo Variable MyP%Index% has value %Value%
set /A Index+=1
goto Loop
:ExitLoop
set /A Index-=1
echo Variables count is: %Count%
endlocal
echo/
pause
The environment variables with undetermined number start all with the string MyP. The first approach runs set MyP in a separate command process in background which outputs all environment variables starting with MyP with their values sorted alphabetically, i.e. the output is for this batch code:
MyP1=1
MyP2=0
MyP3=5
This output is processed line by line by FOR which splits each line up into two substrings because of delims== with first substring being name of the environment variable assigned to loop variable I and everything after first equal sign in the line assigned to next loop variable J according to ASCII table which is the value of the environment variable because of tokens=1*.
The names of the environment variables and their values are output with counting the number of variables starting with MyP which is also output after the FOR loop.
The number of the environment variables must not be consecutive increasing for the first solution.
The second solution uses a loop without FOR. This loop requires that the environment variables have an incrementing number in their names. The loop exits if there is no more environment variable with current index number in name.
The value of the current environment variable is assigned to environment variable Value using an arithmetic expression because in this case a string like MyP1 in the arithmetic expression is interpreted as name of an environment variable whose value should be converted to an integer for evaluation of the expression. The integer result of the arithmetic expression is converted back to a string and assigned to the environment variable left to the equal sign.
The answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? might be helpful to understand the environment variables usage techniques as used here.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
setlocal /?
See also Single line with multiple commands using Windows batch file for an explanation of & operator used on FOR command line to avoid the need of a command block.
I am trying to write a batch file that reads each line in a text file and assigns it (each line) to a variable.
I am using the example found here: Read each line in a txt file and assign variables using windows dos commands, which is:
SETLOCAL EnableDelayedExpansion
SET count=1
FOR /F "tokens=* delims= usebackq" %%x IN ("%TEXT_T%") DO (
SET var!count!=%%x
SET /a count=!count!+1
)
ENDLOCAL
Now, all I want to do is echo the results of each variable, but I just can't seem to be able to do so. Below is what I have, which gives me the results for var0 three times--my text file has three lines. I changed the start of count from the above code from 1 to 0.
#echo off
SETLOCAL EnableDelayedExpansion
SET count=0
FOR /F "tokens=* delims= usebackq" %%x IN ("afile.txt") DO (
SET var!count!=%%x
echo !var%count%!
SET /a count=!count!+1
echo !count!
)
ENDLOCAL
While this code
echo !var%count%!
seems logic, as count is changed inside the loop, you can not access the value of the variable without delayed expansion. But
echo !var!count!!
^...^ ^^
first variable second "variable"
will not work, as the parser is not able to properly determine where each variable reference start/end
You can use any of the two following lines
for %%a in (!count!) do echo !var%%a!
call echo %%var!count!%%
How does it work?
The original idea is good, but there are limits in the syntax. We need delayed expansion over the var.. variable, but also over the count.
1.- In the first solution, we store the delayed expansion of the count variable inside the for replaceable parameter, that is used instead of the count variable in the echo command, keeping the delayed expansion only around the var variable.
!var!count!! => %%a=!count! => !var%%a!
2.- In the second solution, a call command is used. This call causes a double evaluation of the line, first on the line parse to execute the call, second while call execution. That is, the line
call echo %%var!count!%%
is first parsed, and the only variable referenced in it is !count! (a double percent sign is a single escaped percent sign), so the line is translated into (suppose count contains 5)
call echo %var5%
Now, the call is executed, line is parsed again to execute the echo and the value of the correct variable is retrieved.
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