in a batch file I need a list of entries, each with three properties. I solved this just by saying that three consecutive list elements belong to one entry:
set MYLIST = %MYLIST% Entry1Property1 Entry1Property2 Entry1Property3
set MYLIST = %MYLIST% Entry2Property1 Entry2Property2 Entry2Property3
...
No I want to iterate over all entries with a FOR loop. But how do I get all three properties for an entry within a single increment?
This will return only one property by increment:
(for %%G in (%MYLIST%) do (
echo %%G
))
This will return only the properties of the first element:
(for /F "tokens=1,2,3" %%G in (%MYLIST%) do (
echo %%G %%H %%I
))
So how do I get tuples of three properties in each increment?
Related
How can I call in batch a multiple level of array ?
I want to create an array of unique value with multiples data, then I will manage to make an associative array after unyfing the values.
I have a csv file with lot of value like this :
R14201;03h22mn55s;profile;15;
R14201;03h23mn02s;profile,15;
R14201;03h23mn07s;home;15;
R14202;03h23mn12s;profile;12;
R14201;03h23mn16s;home;14;
R14203;03h23mn19s;profile;15;
R14203;03h23mn22s;profile;11;
R14201;03h23mn25s;profile;15;
R14204;03h23mn31s;profile;12;
...
What I want to do :
1 - Get unique reference of RXXX (success)
2 - See changes in third columns, only changes and their order
3 - See different values of last column
So at the end, the desire state will be :
R14201 : [profile, home, profile], [15, 14]
R14202 : [profile], [12]
R14203 : [profile], [15, 11]
R14204 : [profile], [12]
In terme of variable, I will have something like that :
myArray[R14201].NAME=R14201
myArray[R14201].PROFILE[0]=profile
myArray[R14201].PROFILE[1]=home
myArray[R14201].PROFILE[2]=profile
myArray[R14201].NUMBER[0]=15
myArray[R14201].NUMBER[1]=14
myArray[R14202].NAME=R14202
myArray[R14202].PROFILE[0]=profile
myArray[R14202].NUMBER[0]=12
myArray[R14203].NAME=R14203
...
edit :
In term of logic, I think that I will have 2 arrays : my associative array and another one to visit my associative array :
myIteratorArray[0]=R14201
myIteratorArray[1]=R14202
myIteratorArray[2]=R14203
myIteratorArray[3]=R14204
...
myArray[R14201].NAME=R14201
myArray[R14201].PROFILE[0]=profile
...
For now I'm just trying to understand and set my array in batch, I learn the langage 3 days ago.
But the fact is : I know with EnableDelayedExtension that I can use 2 variable imbricated, but I don't know if I can have 3 or more imbricated variables.
Thanks to Aacini on this post, I manage to understand a lot of things about arrays in batch.
On the code bellow, into the second for, I don't know how to call the variable.
#echo off
setlocal EnableDelayedExpansion
set i=0
for %%d in (R14201 R14201 R14201 R14202 R14201 R14203 R14203 R14201 R14204) do (
:: Here I see if myArray[RXXX] is defined
if not defined myArray[%%d] (
set /A i=i+1
set myArray[%%d]=x!i!
:: Here I set an other array in order to loop on this array, to call myArray[RXXX]
:: I will call myArray[RXXX] and RXXX will be stored in myIteratorArray[x].
:: I am sure that myIteratorArray[x] will contain unique value thanks to the if condition
set myIteratorArray[!i!]=%%d
)
)
set numberOfJu=%i%
for /L %%i in (1, 1, %numberOfJu%) do (
::here the problematic call
echo !myArray[!myIteratorArray[%%i]!]!
)
Here my questions :
1 - How can I call a variable with multiple imbricated variable like myArray[myIteratorArray[myIndex]] ?
2 - In order to success, what do you think about my logic ?
3 - Do you think it will be possible to do this in batch ? I have no choice for the language.
Thanks in advance !
when a variable that requires delayed expansion to access is composed of additional delayed variables, an additional for loop can be used to access sub variables:
for /L %%i in (1, 1, %numberOfJu%) do For /f "Delims=" %%u in ("!myIteratorArray[%%i]!") do (
echo !myArray[%%u]!
)
As for defining the array with each unique value, findstr can be used to search the relevent group of the array for the existance of that value before adding it to the array during the definition process.
For an advanced example of handling associative arrays in batch, see here
The above is a template for batch RPG games that uses associative arrays to handle all matters related to inventory, with each item having associated values for things such as value, weight dmg/ defence etc that get updated whenever an item is changed by iterating over the #Equipped group.
the below is a brief script demonstrating how to define an array that can be seached with findstr to achieve an ordered output:
#Echo off & Cd /d "%~dp0"
Setlocal EnableDelayedExpansion
:# replace 'source.csv' with the path to your csv file
For /F "Delims=" %%G in (Source.csv)Do For /F "Tokens=1,3,4 Delims=;," %%1 in ("%%G")Do (
Echo(!$Refs!|%__APPDIR__%Findstr.exe /c:"%%1" > nul || (
Set "$Refs=!$Refs! %%1"
)
Set /A "%%1[i]+=1"
rem compare last defined [reference]
For /f "Delims=" %%v in ('Set /A "!%%1[i]!-1"')Do (
If not "!Ref[%%1][%%v]!"=="%%3" (
Set /A "[i]+=1"
Set "Ref[%%1][!%%1[i]!]=%%3"
Set /A "Ref[%%1]+=1"
Set "$Ref[![i]!]%%1[!Ref[%%1]!].%%2=%%3"
) Else (
Set /A %%1[i]-=1
)))
For %%G in (!$Refs!)Do For /f "Tokens=1,2 Delims==" %%H in ('Set $Ref[ ^| %__APPDIR__%findstr.exe /c:"%%G"')Do (
For /F "Tokens=2 Delims=." %%1 in ("%%H")Do Echo(Reference: %%G type: %%1 value:%%I
)
Example output:
Reference: R14201 type: profile value:15
Reference: R14201 type: home value:14
Reference: R14201 type: profile value:15
Reference: R14202 type: profile value:12
Reference: R14203 type: profile value:15
Reference: R14203 type: profile value:11
Reference: R14204 type: profile value:12
I wrote the following batch file:
setlocal EnableDelayedExpansion
set integer=0
for /f "delims=" %%a in ('where /R C:\ /F python.exe') do (
set Paths[!integer!]=%%a
set /A integer+=1
)
for %%b in (%Paths%) do (
echo %%b
)
The idea is that I want to run where /R C:\ /F python.exe and then store the several paths outputted into an array, I need this because I need to handle these paths later on. So far for testing purposes I called the second half of the script just to make sure the array is working properly.
Your second part is wrong. Since there are no real arrays in batch scripting, you cannot use a for loop similar to something like for each element in array do as used in other languages. Refer to this post for details: Arrays, linked lists and other data structures in cmd.exe (batch) script.
To loop through all (pseudo-)array elements you could do the following instead:
set /A count=integer-1
for /L %%i in (0,1,%count%) do echo Paths[%%i] = !Paths[%%i]!
As suggested by Aacini in a comment, you might change your base-0 to a base-1 (pseudo-)array by moving the line set /A integer+=1 upwards to be before line set Paths[!integer!]=%%a in your first part, so the variable integer already contains the greatest index, which would lead to a slightly simpler second part:
for /L %%i in (1,1,%integer%) do echo Paths[%%i] = !Paths[%%i]!
Alternatively, you could use a for /F loop that parses the output of set Paths[:
for /F "tokens=1* delims==" %%i in ('set Paths[ 2^> nul') do echo %%i = %%j
This might be beneficial:
you do not need to know the total number of elements;
the indexes do not have to be adjacent, they could even be non-numeric;
But there are two caveats:
the (pseudo-)array elements are not returned in ascending order with respect to the indexes, because set Paths[ returns them alphabetically sorted, where the order is 0, 1, 10, 11,..., 2, etc., so the order becomes jumbled as soon as there are indexes with more than a single digit;
in case there exist other variables beginning with that name, like Paths[invalid] or Paths[none, they become returned too;
I have set my arrays like this:
SET ORG[0]=Microsoft
SET ORG[1]=Google
SET ORG[2]=Yahoo
SET ORG[3]=Snapchat
SET ORG[4]=Whatsapp
SET ORG[5]=Facebook
Then I do a find in one of the files to get the value stored in the file:
For /F Tokens^=10^ Delims^=^" %%A in ('Find /I "%Client%"^<"C:\Env\Test\test.xml"') Do Echo "%%A" & SET "ORG=%%A"
Where ORG can be any of the above mentioned array values depending on what's stored in the file. For example say for this instance:
ECHO %ORG% gives Google.
I would like to compare my %ORG% value fetched from the file against the set array values and If its equal then I want my batch script to:
GOTO :Action Else
ECHO "Client Name Not Found"
I am not sure how to compare a variable with the values in the array?
for /f "tokens=1*delims==" %%a in ('set ORG[ 2^>nul ') do if "%%b"=="%ORG" goto action
echo Client not found.
The set command lists the existing variables that start ORG[ in the format org[3]=Snapchat Setting delims to = only assigns ORG[3] to %%a and Snapchat to %%b.
If such a match is found, the client is valid on the list. If the for exhausts all entries, processing simply proceeds to the next instruction in the file.
To make the comparison case-insensitive, use if /i in place of if
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.
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