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)
Related
I'm trying to remove an asterisk from an environmental variable string, but can't seem to do it.
I'm creating an m3u file based around search strings, so for instance I if I want to make an m3u file containing every song with the word love in it, I would enter:
m3u *Love*
And m3u.bat would create the file:
xLovex.m3u
But the regular method of replacing characters does not work with an asterisk. (Though I don't have that problem with the question mark.)
set nam=%nam:*=x%.m3u
Instead creates the filename
x.m3u
The easy answer is no.
The problem that you're encountering stems from the fact that the asterisk * is a special character when used with the SET search and replace method. It matches multiple characters in a limited, but still useful, way. You can learn about that here.
The hard answer is Yes!
I will provide you with two solutions. One an incomplete solution but elegent,
the other complete and inelegent.
Both methods will search for * and replace it with an x.
Both methods will both search and modify the following string:
*love*
The first method that comes to mind is using a 'FOR /L' statement, and requires that you know how many characters long the environmental variable is.
::Major Edit::
I thought I knew the various maximum size strings of environmental variables, but dbenham has taken me to school, shown me a kick-in-the-behind length function, and in the mean time completely reversed my opinions of the two solutions I'm presenting.
Other than for the Windows 95/98/ME limitation of a 256 Character maximum environmental variable size. It seems that all versions of Windows using CMD.EXE have a limitation of 8,192 characters, well below what the documentation suggests.
Both versions require delayed environmental variable expansion, but for two different reasons. One because I'm operating inside a FOR statement. The other because you cannot put a % pair inside another % pair because the command processor matches the second % that it encounters to the first one it encounters, but we need to use a variable inside another variable expression. (You'll see.)
This solution uses the strLen function (in line 3) from DosTips.com that can be found Here. Just slap it into a file called strLen.bat and be amazed at it's speed!
Solution 1: (FOR /L Solution) :: Preferred Solution ::
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
rem calling strLen
call :strLen nam len
for /l %%x in (0,1,%len%) do if not "!nam:~%%x,1!"=="" if "!nam:~%%x,1!"=="*" (
set /a plusone=%%x+1
for /l %%y in (!plusone!, 1, !plusone!) do (
set nam=!nam:~0,%%x!x!nam:~%%y!
)
)
echo %nam%
ENDLOCAL
I think this is a quick and elegant solution It could be sped up by adding the contents of strLen.bat to the routine, but I wanted no confusion as to the author.
If you, for some reason, do not wish to use strLen, then the next quickest method would probably use a GOTO loop.
Solution 2: (Goto Solution)
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
set num=0
:loop
set /a plusone=%num%+1
if "!nam:~%num%,1!"=="*" set nam=!nam:~0,%num%!x!nam:~%plusone%!
set /a num=%num%+1
if not "!nam:~%num%,1!"=="" goto :loop
echo %nam%
EndLocal
Special thanks to dbenham for pointing out the strLen function. It works faster than any batch based function has a right to!
Although there were already some very good and robust ways explained here, I'd still like to add another option for the sake of completion.
It's not as good as the other options but I personally use it in some cases where I'd like to keep the code clean and where I know that it will suffice:
The way it works is by using for /f's delims to cut the string into two parts, which are then put back together, getting rid of the * in the process:
for /f "tokens=1,* delims=*" %%a in ("a*b") do (set string=%%a%%b)
>>> string=ab
Obviously, the downside to this is that it can only be used to remove one *.
To remove more, we can either just use more tokens...
for /f "tokens=1-3,* delims=*" %%a in ("a*b*c*d") do (set string=%%a%%b%%c%%d)
>>> string=abcd
... or we can put the first line in a for /l-loop:
setlocal enableDelayedExpansion
set string=a*b*c*d
for /l %%a in (1, 1, 3) do (
for /f "tokens=1,* delims=*" %%b in ("!string!") do (set string=%%b%%c)
)
>>> string=abcd
Another thing to note is that you can define more than one character in delims, and they will all be removed at once:
for /f "tokens=1,* delims=+-*/" %%a in ("a*-/+b") do (set string=%%a%%b)
>>> string=ab
Another solution to the stated problem is to use a PowerShell replace command within your batch script.
set var=*Love*
echo %var%>var.txt | powershell -command "((get-content var.txt) -replace '[\x2A]','x') -replace '.{1}$' | set-content var.txt"
set /p var=<var.txt
set var=%var%.m3u
echo %var%
In the above code, the second line
writes your string into a text file
calls a PowerShell command to get the contents of that file
replaces the * character with null
overwrites the text file with the new value
Once that is done, you read the value back into your variable.
To further explain the replace command, the first single quotes is what you are searching for. We are using square brackets to identify the * character as a hex character (\x2A is the hex value for *). After the comma, the second set of single quotes contains no value so that the searched object is removed. To prevent a space between xLovex and the .m3u, we have to use -replace '.{1}$' before writing the result to the text file.
Once you are done with the text file, enter a line to delete it.
if exist var.txt del var.txt
Here is an approach that does not walk through all characters of a string, but it uses a for /F loop to split the string at every occurrence of a (sequence of a) certain character. The actual functionality is packed into a sub-routine for easy reuse, so the main section of the following script just contains some code to test:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
::This is the main routine of the script holding code for test and demonstration:
rem // Definition of some sample text to test (note that `%%` becomes one literal `%`):
set "DATA=some text,"^&"&;0'#%%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_"
echo/
call :REPL_CHAR TEXT DATA "*" "?"
setlocal EnableDelayedExpansion
echo(In: !DATA!
echo(Out:!TEXT!
echo/
echo(In: !TEXT!
call :REPL_CHAR TEXT TEXT "=" "/"
echo(Out:!TEXT!
endlocal
endlocal
exit /B
:REPL_CHAR
::This function replaces in a string every occurrence of a sequence of a certain character
::by another character or a string. It even correctly handles the characters `*` and `=`.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every sequence of `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "TST=!%#STR%!" & set "CHR=!CHR:~,1!" & set "INS="
if "!CHR!"=="_" (set "BUF=#" & set "WRK=!TST!#") else (set "BUF=_" & set "WRK=!TST!_")
:REPL_CHAR_LOOP
rem // Check whether the end of the string has been reached:
if not defined TST set "BUF=!BUF:~1,-1!" & goto :REPL_CHAR_NEXT
rem // Split the string at the next sequence of search characters:
for /F tokens^=1*^ delims^=^%CHR%^ eol^=^%CHR% %%S in ("!BUF!!INS!!WRK!") do (
rem // Store the portions before and after the character sequence:
endlocal & set "BUF=%%S" & set "TST=%%T" & set "WRK=%%T" & setlocal EnableDelayedExpansion
)
rem // Loop back and find the next character sequence:
set "INS=!RPL!" & goto :REPL_CHAR_LOOP
:REPL_CHAR_NEXT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
The input and output data of this script (let us call it repl_char_demo.bat) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>/|+([{parens}])-^/equal/to/!_
This is a script that uses for /L loops to walk through all characters of the string, to check each character against a predefined one and replaces it as specified. This method replaces every single matching character rather than sequences. Again the functionality is put into a sub-routine (the main section is dismissed this time):
:REPL_CHAR
::This function replaces in a string every occurrence of one certain character by another
::character or a string. It even correctly handles the characters `*` and `=`, as well as
::sequences of search characters so that every single one becomes replaced.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every single `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "WRK=!%#STR%!" & set "CHR=!CHR:~,1!" & set "BUF="
rem // Loop through all characters and check for match:
if defined WRK for /L %%J in (0,1,63) do for /L %%I in (0,1,127) do (
set /A "POS=%%J*64+%%I" & for %%P in (!POS!) do (
set "TST=!WRK:~%%P,1!" & if not defined TST goto :REPL_CHAR_QUIT
rem // Store character or replacement depending on whether there is a match:
if "!TST!"=="!CHR!" (set "BUF=!BUF!!RPL!") else (set "BUF=!BUF!!TST!")
)
)
:REPL_CHAR_QUIT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
There are actually two nested for /L loops rather than a single one, both of which become broken as soon as the end of the string is reached, using the goto command. Breaking a for /L loop means that it completes iterating in the background although its body is no longer executed. Therefore, using a single loop takes much more time to finish after being broken rather than two nested ones.
The input and output data of this script (with the same main section as above) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>/|+([{parens}])-^/equal//to/!_
See this answer, and with set-ast.bat you'll want to put set-ast nam "x" in your file where needed.
set-ast takes the parameters <variable-to-modify> <string-to-replace-asterisks-with>
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.
I am using a for loop to create textfiles with predefined headers, but I want to append a unique string (alice, bob, etc.) on the 2nd line for each file from a predetermined list.
Set a=alice
Set b=bob
Set c=chris
Rem ...etc. (I have about 30 files with a unique name to go in row 2 of each file)
For /l %%x in (1, 1 , 30) do (echo headerRow1 > file%%x.txt & echo %a% > file%%x.txt)
I dont understand how to automatically insert "bob" for the second iteration (ie. In "file2.txt") and "chris" for the third iteration, and continue for 30 files.
Do I somehow loop the %a% part to increment by one each time. What should I name the string variables to do this? Are these even the right questions?
Thanks for looking.
It seems that the problem you're trying to solve is to be able to iterate between two lists in one for loop, when the for loop can only iterate through one set at a time. The problem is harder in that arrays and lists don't really exist in Batch as first-class citizens.
There are multiple ways of solving this, but one simpler way is to loop through the names, and increment a counter each time through the loop. (This is probably easier than a for /l loop and incrementing the list of names.)
Here is an example.
#echo off & setlocal enabledelayedexpansion
set names=alice bob chris
set x=1
for %%a in (%names%) do (
>file!x!.txt (
echo headerRow
echo %%a
)
set /a x+=1
)
Notes:
Set all your names on a single line, and a for loop can iterate through them.
Set your counter (x) at the start, then increment it at the end of the loop.
I'm using delayed expansion (setlocal enabledelayedexpansion) and the syntax of !x! instead of %x% so that Batch resolves the variable x each time through the loop, instead of once (and only once) the first time it goes through, so that it gets the right incremented value each time.
I hope that helps.
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "targetdir=U:\destdir"
SET "filename1=%sourcedir%\q40171884.txt"
SET "headerrow=This is your header"
SET "line2=This is line two for "
FOR /f "tokens=1*delims=[]" %%a IN ('find /n /v "" "%filename1%"') DO IF "%%b" neq "" (
>"%targetdir%\file%%a.txt" (
ECHO(%headerrow%
ECHO(%line2%%%b
TYPE "%sourcedir%\thefilecontainingtheremainder.txt"
)
)
GOTO :EOF
You would need to change the settings of sourcedir and targetdir to suit your circumstances.
I used a file named q40171884.txt containing your names for my testing.
Uses the file "%sourcedir%\thefilecontainingtheremainder.txt" to contain the body of the data to be assigned to the output file. Naturally, this could be anywhere.
Essentially, take the filename containing your names, and use find /n /v "" to prefix each line with [sequencenumber]. Using delims of [], assign the prefix to %%a and the remainder of the line (the name) to %%b.
The first line output by find will report the filename, but won't contain [] (unless you get silly and engineer it that way), so %%b in that case will be empty. We skip that line, and for the others, %%a contains the number and %%b the name.
Then create new output files "filenumber.txt" and the two special lines - the second with %%b (the name) appended. Then regurgitate the remainder of the required text by simply typeing it.
%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.
I have an array in a batch file which contains several file directories. I would like to remove the first three elements of the array and have everything shift over (i.e. the fourth element takes the place of the first, the fifth element takes the place of the second, sixth takes the place of the third, etc). Is this something that can be done in a Batch script?
Example:
[directoryA, directoryB, directoryC, directoryD, directoryE, directoryF]
is changed to:
[directoryD, directoryE, directoryF]
Here is the code I have for the "array":
set paramCount=0
for %%x in (%*) do (
set /A paramCount+=1
set "dirs[!paramCount!]=%%x"
)
The best was I can think of to do this is to have two "arrays", one for the data, another for whether the data is currently valid. The sample code below creates one dirs "array" holding any command line parameters and another valid "array" and then prints only "valid" data from dirs.
valid[i] will be equal to 1 if and only if the data in dirs[i] is valid. Any other value in valid[i] will indicate that the data in dirs[i] is to be ignored.
#echo off
setlocal enabledelayedexpansion
set paramCount=0
for %%x in (%*) do (
echo %%x
set /A paramCount+=1
REM Add data to dirs array
set "dirs[!paramCount!]=%%x"
REM Add valid flag to valid array
set "valid[!paramCount!]=1"
)
echo Printing...
REM Delete element at index 2
set valid[2]=0
REM Print the modified array
for /L %%a in (1 1 %paramCount%) do (
IF !valid[%%a]!==1 (echo !dirs[%%a]!)
)
Example Output:
$>deletetest.bat 1 2 3 4
1
2
3
4
Printing...
1
3
4
Excuse me. I would like to state some aclarations about this topic.
You have not indicated where your "array" come from. You may store it in a variable, for example:
set dirs=directoryA directoryB directoryC directoryD directoryE directoryF
However, this is not an array, but a list. You may create this list from the parameters of a subroutine this way:
set dirs=%*
If you want to remove the first three elements from this list, you may do that this way:
for /F "tokens=3*" %%a in ("%dirs%") do set dirs=%%b
Another possibility is that the directories were passed to a subroutine as a parameters list:
call :subroutine directoryA directoryB directoryC directoryD directoryE directoryF
I think this is the case based on your example. In this case it is very easy to "remove" the first three parameters via three shift commands:
:subroutine
rem Remove first three parameters:
shift
shift
shift
rem Process the rest of parameters:
:nextParam
if "%1" equ "" goto endParams
echo Next param is: %1
shift
goto nextParam
:endParams
However, if you have a "real" array (with numeric subscripts that start at 1) that may also be created from the parameters list for a subroutine this way (like in your example):
set paramCount=0
for %%x in (%*) do (
set /A paramCount+=1
set "dirs[!paramCount!]=%%x"
)
Then you may remove the first three elements this way:
for /L %%i in (4,1,%paramCount%) do (
set /A j=%%i-3
set dirs[!j!]=!dirs[%%i]!
)
set /A paramCount-=3