How to remove duplicate comma separated values from variable using batch file? - batch-file

Want to remove the duplicate values from machines variable in below batch file:
#echo off
set machines=node1,node2,node3,node2,node4,node1,node7,node6,node4
Expected output:
node1,node2,node3,node4,node6,node7

One way to accomplish your task:
Iterate the machines with a for and set them to an array,
this overwrites doublette entries.
Read the (alpha sorted) array back and store in the original variable (requires delayed expansion).
:: Q:\Test\2018\09\20\SO_52417320.cmd
#Echo off & Setlocal EnableDelayedExpansion
Set machines=node1,node2,node3,node2,node4,node1,node7,node6,node4
:: clear array machine[], then fill it
For /f "tokens=1 delims==" %%M in ('Set machine[ 2^>Nul') do Set "%%M="
For %%M in (%machines%) do Set "machine[%%M]=%%M"
Set machine[
Echo:
Set "machines="
For /f "tokens=2 delims==" %%M in ('Set machine[') do Set "machines=!machines!,%%M"
Echo:%machines:~1%
Sample output:
> SO_52417320.cmd
machine[node1]=node1
machine[node2]=node2
machine[node3]=node3
machine[node4]=node4
machine[node6]=node6
machine[node7]=node7
node1,node2,node3,node4,node6,node7

A short way to do it (this doesn't sort the machines variable, only removes the duplicates):
#echo off& setlocal EnableDelayedExpansion
set machines=node1,node2,node3,node2,node4,node1,node7,node6,node4
set "tmac=,"& for %%a in (%machines%) do if "!tmac:,%%a,=!"=="!tmac!" set "tmac=!tmac!%%a,"
set machines=%tmac:~1,-1%

Related

Batch: Parsing out file path from dynamic array [duplicate]

This question already has an answer here:
Batch recursing through folders & populating an array
(1 answer)
Closed 6 years ago.
Looking to parse out the path from a specific point & then use that to populate the dynamic array.
Example:
Folder tree:
C:\Main\folder1
C:\Main\folder2\folder2-1
C:\Main\folder3\folder3-1\folder3-2
Desired result:
Array[1]=folder1
Array[2]=folder2
Array[3]=folder2\folder2-1
Array[4]=folder3
Array[5]=folder3\folder3-1\
Array[6]=folder3\folder3-1\folder3-2
This is the working code below which returns fine but in full paths:
#echo off
setlocal EnableDelayedExpansion
SET folders=C:\Main
rem Populate the array with existent files in folder
set i=0
for /r "%folders%" /d %%a in (*) do (
set /A i+=1
set list[!i!]=%%a
)
set foldnum=%i%
rem Display array elements
for /L %%i in (1,1,%foldnum%) do (SET array[%%i]=!list[%%i]!)
for /F "tokens=2 delims==" %%f in ('set array[') do echo %%f
You pass an absolute path to the FOR loop. But even with a relative path the FOR loop does too much and converts it to an absolute path.
The trick here is to replace the absolute path in the FOR loop.
Create a copy of the loop variable in a real variable
set AA=%%a
Then replace prefix+backslash by nothing in the list "array'
set list[!i!]=!AA:%folders%\=!
full fixed code:
#echo off
setlocal EnableDelayedExpansion
SET folders=C:\Main
rem Populate the array with existent files in folder
set i=0
for /r "%folders%" /d %%a in (*) do (
set /A i+=1
rem create a copy of the loop variable in a real variable
set AA=%%a
rem replace prefix+backslash by nothing in a the list "array"
set list[!i!]=!AA:%folders%\=!
)
set foldnum=%i%
rem Display array elements
for /L %%i in (1,1,%foldnum%) do (SET array[%%i]=!list[%%i]!)
for /F "tokens=2 delims==" %%f in ('set array[') do echo %%f
then you get all the dirs of %folders% in a relative way.
A little bit of Tom Foolery.
#echo off
setlocal EnableDelayedExpansion
SET folders=C:\Main
subst B: %folders%
B:
set i=0
for /r /d %%G in (*) do (
set /A i+=1
FOR /F "tokens=* delims=\" %%H IN ("%%~pnG") do set "array[!i!]=%%~H"
)
C:
subst B: /D
set foldnum=%i%
rem Display array elements
for /F "tokens=2 delims==" %%f in ('set array[') do echo %%f
pause

Batch: Checking if any variables are equal

Thanks to Aacini, I now have a way to sort variables from greatest to least.
Link:
Comparing and ordering multiple numbers in batch
However, if 2 or more of my variables are the same value, they won't be sorted. I'm trying test to see if two variables in the set are equal. I tried using if statements against each variable in any combination I could think of, but that isn't very efficient and is hard to change.
Is there a way I can achieve this?
#echo off
setlocal EnableDelayedExpansion
set speed1=190
set speed2=78
set speed3=78
set speed4=23
rem Get the descending order of previous elements via "order" array
for /L %%i in (1,1,4) do (
set /A num=1000-speed%%i
set order!num!=%%i
)
rem Show the elements of "speed" array in descending order
for /F "tokens=2 delims==" %%i in ('set order') do (
echo speed%%i = !speed%%i!
)
Output will only display:
speed1 = 190
speed3 = 78
speed4 = 23
Excuse me. I don't know if you are really interested to know if two elements have the same value, or just to fix the bug of my previous solution (that don't include the elements with the same value), so I opted for solve previous bug:
#echo off
setlocal EnableDelayedExpansion
set speed1=190
set speed2=78
set speed3=78
set speed4=23
rem Get the descending order of previous elements via "order" array
REM Insert a second index to differentiate elements with the same value
for /L %%i in (1,1,4) do (
set /A num=1000-speed%%i
set order[!num!][%%i]=%%i
)
rem Show the elements of "speed" array in descending order
for /F "tokens=2 delims==" %%i in ('set order') do (
echo speed%%i = !speed%%i!
)
#echo off
setlocal enableextensions enabledelayedexpansion
set speed1=190
set speed2=78
set speed3=78
set speed4=23
for /f "usebackq tokens=1,2 delims=/" %%a in (`
cmd /q /e /c "for /f tokens^=1^,2^ delims^=^= %%c in ('set speed') do (set /a %%d + 10000000 & echo /%%c)"
^| sort /r
`) do (
set /a "value=%%a-10000000"
echo %%b=!value!
)

Create variables dynamically based on the number of tokens on a FOR /F loop

I have a file from which name i want to get information.
LOCAL DEPT, FIRST-FLOOR-DEPT.bat
I was already helped to separate using FOR /F loops the value i needed using the comma as the delimiter, but from that second part each of the word delimited by dash(-) i want assign them to a variable and from all those dynamically created variables make 1 that will unify them all. It sounds a bit weird i just can explain better. See the code i have so far testing and researching...
Setlocal EnableDelayedExpansion
SET c=0
SET getname=%~n0
for /f "tokens=2 delims=," %%F IN ("%getname%") DO (
for /f "tokens=1* delims=- " %%G IN ("%%F") DO (
SET /a c+=1
SET step!c!=%%G
)
)
for /L %%G in (!c!, -1, 1) do SET th=!step%%G!\
#echo %th%
Im expecting %th% to echo something like FIRST\FLOOR\DEPT but instead im getting only the first token like FIRST\
IMPORTANT: I have several files like this in which the amount of names sometimes is NAME, A-A other times is NAME, A-A-A or more so it cant be static :( in any way
Thanks in advance
Use string substitution:
Setlocal EnableDelayedExpansion
SET getname=%~n0
for /f "tokens=2 delims=," %%F IN ("%getname%") DO (
set th=%%F
rem Replace - with \
set th=!th:-=\!
rem trim the first space
set th=!th:~1!
)
echo %th%
You should use %random% for dynamic numbers. Such as:
set /a guns=%random%
echo you've %guns% guns! congratulations!
for /l %%I in (1,1,10) do set randnum=%%i

How to get the list of filenames in a directory and store that in a variable using cmd commands

I need to get all the filenames in a directory and store them in some variable from a command line.
I came across this
`dir /s /b > print.txt`
but this prints the file names to a txt file.
How can I store these names in a variable?
I'm assuming you really mean Windows batch file, not DOS.
Batch environment variables are limited to 8191 characters, so likely will not be able to fit all the file paths into one variable, depending on the number of files and the average file path length.
File names should be quoted in case they contain spaces.
Assuming they fit into one variable, you can use:
#echo off
setlocal disableDelayedExpansion
set "files="
for /r %%F in (*) do call set files=%%files%% "%%F"
The CALL statement is fairly slow. It is faster to use delayed expansion, but expansion of %%F will corrupt any value containing ! if delayed expansion is enabled. With a bit more work, you can have a fast and safe delayed expansion version.
#echo off
setlocal disableDelayedExpansion
set "files=."
for /r %%F in (*) do (
setlocal enableDelayedExpansion
for /f "delims=" %%A in ("!files!") do (
endlocal
set "files=%%A "%%F"
)
)
(set files=%files:~2%)
If the file names do not fit into one variable, then you should resort to a pseudo array of values, one per file. In the script below, I use FINDSTR to prefix each line of DIR ouptut with a line number prefix. I use the line number as the index to the array.
#echo off
setlocal disableDelayedExpansion
:: Load the file path "array"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Access the values
setlocal enableDelayedExpansion
for /l %%N in (1 1 %file.count%) do echo !file.%%N!
As #Matt said, use a batch file.
setlocal enabledelayedexpansion
set params=
for /f "delims=" %%a in ('dir /s/b') do set params=!params! %%a
setlocal enableDelayedExpansion
set /a counter=0
for /f %%l in ('dir /b /s') do (
set /a counter=counter+1
set line_!counter!=%%l
)
set line_
If you want to store all in one variable check this:
Explain how dos-batch newline variable hack works

read from file via batch file

I have a file.txt that contains the paths of the oracles that installed on my machine.
from the registry
example this file contains:
ORACLE_HOME REG_SZ C:\oracle\product\11.2.0\dbhome_1
ORACLE_HOME REG_SZ C:\oracle\product\11.2.0\dbhome_2
I want via my batch file to insert all the paths of the oracles into a list or something like list.
How can I do it in batch file?
thanks!
Assuming you want a "list" of values in memory, not in a file:
I think most people prefer an "array" of values that can be accessed via an index value. (Note - batch does not have formal arrays, but they can be emulated).
The following simple code works great as long as none of the home paths contain !. Expansion of %%B will be corrupted if it contains ! and delayed expansion is enabled.
#echo off
setlocal enableDelayedExpansion
:: Read the file and create an "array" of home paths
:: This will fail if any of the paths contain !
set /a cnt=0
for /f "usebackq tokens=2*" %%A in ("file.txt") do (
set /a cnt+=1
set "home.!cnt!=%%B"
)
:: Access the "array" members
for /l %%N in (1 1 %cnt%) do echo !home.%%N!
You could probably run the above code for years in many environments and never run into a problem. But someone somewhere might include ! in the Oracle home path. There are a number of strategies to fix the above to deal with !. Below are three options:
Option 1 - The least amount of code, but the slowest due to CALL
#echo off
setlocal disableDelayedExpansion
:: Read the file and create an "array" of home paths
:: This will safely process all paths, regardless of value
set /a cnt=0
for /f "usebackq tokens=2*" %%A in ("file.txt") do (
set /a cnt+=1
call set "home.%%cnt%%=%%B"
)
:: Access the "array"
setlocal enableDelayedExpansion
for /l %%N in (1 1 %cnt%) do echo !home.%%N!
Option 2 - An interesting and efficient method using FINDSTR to count the rows.
#echo off
setlocal disableDelayedExpansion
:: Read the file and create an "array" of home paths
:: This will safely process all paths, regardless of value
set /a cnt=0
for /f "tokens=1,3* delims=: " %%A in ('findstr /n "^" "file.txt"') do (
set "home.%%A=%%C"
set "cnt=%%A"
)
:: Access the "array" members
setlocal enableDelayedExpansion
for /l %%N in (1 1 %cnt%) do echo !home.%%N!
Option 3 - An efficient method that uses delayed expansion toggling, but the most code
#echo off
setlocal disableDelayedExpansion
:: Read the file and create an "array" of home paths
:: This will safely process all paths, regardless of value
set /a cnt=0
for /f "usebackq tokens=2*" %%A in ("file.txt") do (
set /a cnt+=1
setlocal enableDelayedExpansion
for %%N in (!cnt!) do (
endlocal
set "home.%%N=%%B"
)
)
:: Access the "array"
setlocal enableDelayedExpansion
for /l %%N in (1 1 %cnt%) do echo !home.%%N!
It is also possible to have a list of home paths in a single variable. Each path should be enclosed by quotes. The paths could be delimited by space, comma, semicolon, equal, or tab. I chose space.
The size of the list is limited because the maximum size of a batch environment variable is ~8191 bytes. This solution is also relatively slow due to the CALL. Neither of these issues are likely to be a problem in the real world.
#echo off
setlocal disableDelayedExpansion
:: Read the file and create a space delimited list of quoted home paths
:: This will safely process all paths, regardless of value
for /f "usebackq tokens=2*" %%A in ("file.txt") do (call set list=%%list%% "%%~B")
:: optional - remove leading space
(set list=%list:~1%)
:: Display the list
echo list=%list%
:: Access the list members
for %%F in (%list%) do echo %%~F
for /f "tokens=3 delims= " %%a in (file.txt) do echo %%a >>paths.txt

Resources