I'm messing around in Batch and writing code that loops through files in a directory and adds the last four characters of the file name to an array. The problem is that there are duplicates in my array. Is there a way to only add the value to an array if it doesn't exist yet?
@echo off
setlocal enableDelayedExpansion
set i=0
for %%f in (*00.DIF) do (
set fname=%%~nf
set year=!fname:~-4!
::echo !year!
set Obj[!i!].name=!year!
set /a i=!i!+1
)
set lastindex=!i!
for /L %%f in (0,1,!lastindex!) do (
echo !Obj[%%f].name!
)
As already noted, within a block statement (a parenthesised series of statements)
, REM
statements rather than the broken-label remark form (:: comment
) should be used because labels terminate blocks, confusing cmd
. ::
is a broken label because you cannot reach ::
with a goto
, but it's a label nonetheless.
Use set "var=value"
for setting string values - this avoids problems caused by trailing spaces. Don't assign "
or a terminal backslash or Space. Build pathnames from the elements - counterintuitively, it is likely to make the process easier. Use set /a var=value
to set numeric values.
I prefer to avoid ADFNPSTXZ (in either case) as metavariables (loop-control variables)
ADFNPSTXZ are also metavariable-modifiers which can lead to difficult-to-find bugs
(See for/f
from the prompt for documentation)
The !var!
syntax is only required to access the modified value of var
where var
is varied within the code block (parenthesised sequence of commands).
Hence, rewritten code:
@ECHO OFF
setlocal enableDelayedExpansion
set /A i=0
SET "chosen="
REM for %%e in (*00.DIF) do (
for %%e in (1234 0011 4567 7890 0011 4183) do (
set fname=%%~ne
set year=!fname:~-4!
REM echo !year!
set Obj[!i!].name=!year!
set /a i+=1
SET "unaltered=Y"
FOR %%y IN (!chosen!) DO IF DEFINED unaltered IF "%%y"=="!year!" (
SET "unaltered="
set /a i-=1
set "Obj[!i!].name="
)
IF DEFINED unaltered SET "chosen=!year! !chosen!"
)
set /a lastindex=i-1
for /L %%e in (0,1,%lastindex%) do (
echo !Obj[%%e].name!
)
GOTO :EOF
New variable chosen
is set to nothing.
I've replaced your first for
loop with a loop that simply processes a series of numbers for testing purposes. I've also change the metavariable
throught from f
to e
.
Use more modern syntax to increment i
. see set/?
from the prompt or endless SO items for docco.
New loop on %%y
to search chosen
for previously-assigned values. Note the use of unaltered
as a Boolean - It's either defined as Y
or undefined. If it's defined, simply reverse the increment of i
and delete the "array" string-value assigned. Using Boolean
Then, if the value in year
is new, unaltered
is defined and the new value of year
is appended to the list of values in chosen
.
After the entire list is processed, lastindex
is set to i
- 1 since i
points to the next element to be filled.
--- edit -----
Having recaffienated, using Stephan's findstr
idea...
@ECHO OFF
setlocal enableDelayedExpansion
set /A i=0
SET "chosen="
REM for %%e in (*00.DIF) do (
for %%e in (1234 0011 4183 4567 7890 0011 7890 7890 7890 4183) do (
set fname=%%~ne
set year=!fname:~-4!
REM echo !year!
ECHO !year!|FINDSTR ": !chosen!" >NUL
IF ERRORLEVEL 1 (
set Obj[!i!].name=!year!
set /a i+=1
SET "chosen=!year! !chosen!"
)
)
set /a lastindex=i-1
for /L %%e in (0,1,%lastindex%) do (
echo !Obj[%%e].name!
)
GOTO :EOF
Here, if year
is not in the string chosen
then findstr
will set errorlevel
to 1
; if it is then errorlevel
will be set to 0
.
findstr
by default will find any of the space-separated strings, but it doesn't like an empty string, so a colon is added to the list as a dummy; a colon is impossible in the name part of a filename.
Only if yesr
is missing from the list will it be added.
We don't need the actual output of findstr
, just that it sets errorlevel
, so the output is sent to nowhere by the >nul
.