Batch: loop recursively through directory and sort filenames - arrays

I want to loop recursively through a directory and have it's filenames echoed.
I ran into the 1 vs 01 name trouble.
Say, I have this:
D:\Downloads\prefixorder\order\1.txt
D:\Downloads\prefixorder\order\10.txt
D:\Downloads\prefixorder\order\2.txt
D:\Downloads\prefixorder\order\3.txt
D:\Downloads\prefixorder\order\1\new.txt
D:\Downloads\prefixorder\order\10\new.txt
D:\Downloads\prefixorder\order\2\new.txt
D:\Downloads\prefixorder\order\order\1.txt
D:\Downloads\prefixorder\order\order\10.txt
D:\Downloads\prefixorder\order\order\2.txt
D:\Downloads\prefixorder\order\order\20.txt
D:\Downloads\prefixorder\order\order\3.txt
D:\Downloads\prefixorder\order\order2\1.txt
D:\Downloads\prefixorder\order\order2\10.txt
D:\Downloads\prefixorder\order\order2\2.txt
D:\Downloads\prefixorder\order\order2\20.txt
D:\Downloads\prefixorder\order2copy\1.txt
D:\Downloads\prefixorder\order2copy\10.txt
D:\Downloads\prefixorder\order2copy\2.txt
D:\Downloads\prefixorder\order2copy\20.txt
D:\Downloads\prefixorder\order3\1.txt
D:\Downloads\prefixorder\order3\10.txt
D:\Downloads\prefixorder\order3\2.txt
D:\Downloads\prefixorder\order3\20.txt
D:\Downloads\prefixorder\1.txt
D:\Downloads\prefixorder\10.txt
D:\Downloads\prefixorder\2.txt
and I want to have 10 listed below 1 in each folder (and the folder being sorted like this, too).
Basically looking like this:
D:\Downloads\prefixorder\1.txt
D:\Downloads\prefixorder\2.txt
D:\Downloads\prefixorder\10.txt
D:\Downloads\prefixorder\order\1.txt
D:\Downloads\prefixorder\order\2.txt
D:\Downloads\prefixorder\order\3.txt
D:\Downloads\prefixorder\order\10.txt
D:\Downloads\prefixorder\order\1\new.txt
D:\Downloads\prefixorder\order\2\new.txt
D:\Downloads\prefixorder\order\10\new.txt
D:\Downloads\prefixorder\order\order\1.txt
D:\Downloads\prefixorder\order\order\2.txt
D:\Downloads\prefixorder\order\order\3.txt
D:\Downloads\prefixorder\order\order\10.txt
D:\Downloads\prefixorder\order\order\20.txt
D:\Downloads\prefixorder\order\order2\1.txt
D:\Downloads\prefixorder\order\order2\2.txt
D:\Downloads\prefixorder\order\order2\10.txt
D:\Downloads\prefixorder\order\order2\20.txt
D:\Downloads\prefixorder\order2copy\1.txt
D:\Downloads\prefixorder\order2copy\2.txt
D:\Downloads\prefixorder\order2copy\10.txt
D:\Downloads\prefixorder\order2copy\20.txt
D:\Downloads\prefixorder\order3\1.txt
D:\Downloads\prefixorder\order3\2.txt
D:\Downloads\prefixorder\order3\10.txt
D:\Downloads\prefixorder\order3\20.txt
So somewhat the same order as the default any win7 explorer display sorted after ascending alphabet. (Though in that version the files of a root files get to show below the folders, but that doesn't really matter).
A nice bonus would be, that this output comes no matter what object I dragged the dropped it from.
I found this, which solves this problem nicely for one folder:
Read files in directory in order of filename prefix with batch?
The diffrence to that problem that I want this recursively and while multiple files/folders can be dropped.
My current (buggy) modification looks like this:
#echo off
setlocal EnableDelayedExpansion
rem Create an array with filenames in right order
if [%1]==[] goto :eof
:loop
for /f "tokens=* delims=" %%a in ('dir "%~1" /a-d /s /b') do (
for /F "delims=-" %%i in ("%%a") do (
set "number=00000%%~ni"
set "file[!number:~-6!]=%%a"
)
)
rem Process the filenames in right order
for /F "tokens=2 delims==" %%f in ('set file[') do (
echo %%f
)
shift
if not [%1]==[] goto loop
#pause
the most buggy line in question would seem to be
set "file[!number:~-6!]=%%a"
whose array parameter in fact I don't even really begin to understand. My guess anyhow is that the array's entries are overwritten in each loop, because the parameters are pretty much the same in each loop.
I had also supected that the %%~ni is probably the cause of the overwriting, since the filenames inside the folders are all the same. Using %%~ni seems to me pbviously wrong, but using %%i simply doesn't do any sorting anymore and echoes out 10 before 1, which is even more wrong.
It works however if multiple folders are dragged, since they are in seperate echo loops. I tried putting in the echo loop within the first outer loop before and after the first inner loop. While it does make it that nothing gets omitted, it's not sorting properly at all.
Back to the major question: how do I solve that recursively and with mutiple files/folders dragged, that sorts the filenames and foldernames.
foldernames weirdly are of no prolem when recursively in one folder. It become a problem, when multiple folders/files are dragged, since it then starts off with the dragged object as the first argument.
Do I need to use an array of arrays or something like that? (Tried looking into it, didn't really get how that is possible in batch, yet, through.) Or is there any other way to do this?

Not even sure this is what you need, but just if it can help ...
#echo off
:: Without arguments, just list current folder
if "%~1"=="" (
call :recursiveSortedFileFolderList "%cd%"
goto :eof
)
:: Else, create a list of elements to sort
set "tempFile=%temp%\%~nx0.tmp"
call :generateList %* > "%tempFile%"
call :recursiveSortedFileFolderList "%tempFile%"
del "%tempFile%" >nul
goto :eof
:generateList
echo(%~1
if not "%~2"=="" shift & goto :generateList
exit /b
:recursiveSortedFileFolderList startFolder
setlocal enableextensions disabledelayedexpansion
:: Prepare padding base
set "pad=#################################"
set "pad=%pad%%pad%"
set "pad=%pad%%pad%"
set "pad=%pad%%pad%"
:: paddings for numeric or alphabetic prefixed files and folders
set "_NPad=%pad%"
set "_APad=%pad:#=$%"
:: start work
call :_doRecursiveSortedFileFolderList "%~1"
:: cleanup and exit
endlocal
exit /b
:_doRecursiveSortedFileFolderList folder
:: adjust environment for current folder
setlocal
set "timestamp=%time::=%"
set "tempFile=%temp%\%~nx0.%random%%random%%random%%random%.%timestamp:,=%.tmp"
set "folder=%~1" & if not defined folder ( set "folder=." ) else ( set "folder=%~f1" )
:: determine if we are handling a folder or a file with elements in it
if exist "%folder%\" (
set "cmd=dir /b ""%folder%\*"" 2^>nul"
) else if exist "%folder%" (
set "cmd=more ""%folder%"" "
set "folder="
for /f "tokens=* usebackq" %%a in ("%folder%") do if not defined folder (
for /f "tokens=*" %%b in ("%%~dpa\.") do set "folder=%%~dpnxb"
)
) else (
endlocal
exit /b
)
:: For each element in the indicated folder/file, prefix it with the adequated prefix
:: depending if it is a file or a folder, and send the output to a temporary file
:: that will be sorted (using the adecuate prefix), and processed
(for /f "tokens=*" %%a in ('%cmd%') do (
:: determine if file or folder
if exist "%folder%\%%~nxa\" ( set "type=f" ) else ( set "type=a" )
:: determine correct padding
if "%%~na" geq "a" ( set "name=%_APad%:%%~na" ) else ( set "name=%_NPad%:%%~na" )
:: generate final padded record
setlocal enabledelayedexpansion
echo(!type!:!name:~-260!%%~xa
endlocal
))> "%tempfile%"
:: Sort the temporary file and for each element on it, if it is a file, echo to console,
:: else it is a folder and a recursive call is made to process it
for /f "tokens=1,2,* delims=:" %%a in ('sort /L "C" "%tempfile%"') do (
if "%%a"=="a" (echo(%folder%\%%c) else (call %0 "%folder%\%%c")
)
:: clean and exit
endlocal & del "%tempfile%" > nul 2>nul
exit /b

Related

Batchfile to rename files

Sadly I couldn't yet manage find a working solution but hopefully this time.
Long story short, we got a printer and we are currently unable to configure the scanfolder to our network drives. So I need a script to rename and move the files but keep them all.
As far as I managed to come I got a smart idea to move the files into a firstfolder to avoid that the files are overwritten. Next I need to either rename the files with a counter or move them and keep them all.
I choose the rename option since this seems more simple but I ran into one big issue.
I have no idea how I make it work. So what I'm trying is to first set the variable and do a first test calculation. Just for a first quick test. Now it already works for the first count but sadly it doesn't count up as hoped since he does the rename for all the files before increasing the value of the variable so only one file is renamed.
#ECHO off
::Defining Variables
setlocal EnableDelayedExpansion
SET N=0
ECHO %N% Hi Not rename
SET /a N=%N%+1
FOR /L %%A in (1,1,10) DO (
Echo !N! hi
RENAME "C:\Users\smorheng\Desktop\1\*.pdf" "Test.?????.!N!.*" | SET /a N=!N!+1
ECHO !N! Hi Not rename
timeout 3 /nobreak > nul
)
ECHO RENAME DONE
timeout 50 /nobreak > nul
About 20 Files are renamed to something like Test.1.pdf, Test.2.pdf .... Test.20.pdf and then moved to their destination.
The moving is not an issue but if I could manage to get this feature working I can adapt it to whatever I need.
You can simply move the files, but first check if the file exists in destination, if it does, rename it using a numeric value after the name. Here is something that might work. You just need to change source and destination folder below. The actual move will not occur as I added echo to the second last and last line to demonstrate what it will do, if it works, simply remove echo from both lines.
#echo off
setlocal enabledelayedexpansion
set "source=C:\Users\smorheng\Desktop\1\"
set "dest=D:\destination\folder"
set /a cnt=0
for /f "tokens=*" %%a in ('dir /S /B /A-D "%source%*.pdf"') do for /f "tokens=*" %%b in ('dir /B "%%a"') do if exist "%dest%\%%b" (
set "ext=%%~xa"
set "fname=%%~na"
if exist "%dest%\!fname!(!cnt!)!ext!" (set /a cnt=!cnt!+1)
set /a cnt=!cnt!+1
echo move "%%a" "%dest%\!fname!(!cnt!)!ext!"
) else echo move "%%a" "%dest%\%%b"
Also note, this will recursively move all files from within the directory tree, if you only want to go into the first directory, simply remove /S from the for loop which will then simply become dir /B /A-D "%source%*.pdf"
Edit
As for your for /L loop (mentioned in comment)
Rather have a label and permanently goto it after completed. Here is a simple example of something like that, copy it to a script and run it, see the result:
#echo off
:label
echo Hi, this will run every 6 seconds and print this line. (infinitely).
timeout 6>nul
goto :label
So technically you can do the exact same for your loop, for instance:
#echo off
:label
setlocal enabledelayedexpansion
set "source=C:\Users\smorheng\Desktop\1\"
set "dest=D:\destination\folder"
set /a cnt=0
for /f "tokens=*" %%a in ('dir /S /B /A-D "%source%*.pdf"') do for /f "tokens=*" %%b in ('dir /B "%%a"') do if exist "%dest%\%%b" (
set "ext=%%~xa"
set "fname=%%~na"
if exist "%dest%\!fname!(!cnt!)!ext!" (set /a cnt=!cnt!+1)
set /a cnt=!cnt!+1
echo move "%%a" "%dest%\!fname!(!cnt!)!ext!"
) else echo move "%%a" "%dest%\%%b"
endlocal
timeout 6>nul
goto :label
Try replacing "RENAME" to SET newname see if that helps?
EDIT or try a vbs script instead of a batch?
`Set objFS = CreateObject("Scripting.FileSystemObject")
strFolder="c:\test"
Set objFolder = objFS.GetFolder(strFolder)
For Each strFile In objFolder.Files
If objFS.GetExtensionName(strFile) = "jpg" Then
strFileName = strFile.Name
If InStr(strFileName,"XXXXXXX") > 0 Then
strNewFileName = Replace(strFileName,"XXXXX","YYYYY")
strFile.Name = strNewFileName
End If
End If
Next `

batch script to copy files listed in a txt document and keep duplicates

I am trying to copy thousands of image files listed in a text file from multiple folders to one folder. The script I have works if all the file names were different. How do I get it to rename duplicates or rename all of them? Either will work as the name isn't important.
Example list
G:\research_data\an\an01\DCIM\100MSDCF\DSC04450.JPG
G:\research_data\an\an01\DCIM\100MSDCF\DSC04076.JPG
G:\research_data\an\an01\DCIM\100MSDCF\DSC03141.JPG
G:\research_data\an\an01\DCIM\120MSDCF\DSC04840.JPG
G:\research_data\an\an02\DCIM\100MSDCF\DSC04450.JPG
G:\research_data\an\an02\DCIM\112MSDCF\DSC04076.JPG
G:\research_data\an\an03\DCIM\102MSDCF\DSC03141.JPG
G:\research_data\an\an03\DCIM\105MSDCF\DSC04450.JPG
G:\research_data\an\an03\DCIM\106MSDCF\DSC04076.JPG
code:
#echo off
for /f "tokens=* delims=" %%a in ('type "L:\an_2017\image_list.txt"') do xcopy /hrkvy "%%a" "L:\an_2017"
pause
I see the issue you're running into. Looks like you're trying to transfer images from various directories with some containing the same file names. If you wish to keep both files, you could use an FOR statement to read your text file and rename the file at hand to DSC{Count} from 1+.
Using enabledelayedexpansion we can use set /a count += 1 to count up one number and rename %%a to a number. Please keep in mind I'm using %%~xa to get the extension of each file from the textfile. More info here: Parameter Extensions
The script bellow will read all file locations in the image_list.txt file and will copy each item to it's target directory with a new name of DSC1-DSCinfinity based on how many items are in image_list.txt. This avoids any issue's with duplicate names in your text file.
#ECHO OFF
#setlocal enableextensions enabledelayedexpansion
rem configure directories
set "source=L:\an_2017\image_list.txt"
set "target=L:\an_2017"
rem rename files to DSC{Count}
set /a count = 1
for /f "tokens=* delims=" %%a in ('type "%source%"') do (
copy "%%a" "%target%\DSC!count!%%~xa"
set /a count += 1
)
goto :EOF
In my example below, I am not using the file as a source for the jpg's, though you can still change that it you want. Instead, I dir /s through your DCIM directory, find all jpg files. Then I test the destination directory to see if the file exists, if it does, I increment it with a (n) at the end, if it does not exist, I just copy the file. This way you will have files without numeric increments (duplciates) It will not add a numeric value to each file. We will only do this for duplicates.
#echo off
setlocal enabledelayedexpansion
set "source=G:\research_data\an\an01\DCIM\"
set "dest=L:\an_2017\"
set /a cnt=0
for /f "tokens=*" %%a in ('dir /S /B /A-D "%source%*.jpg"') do for /f "tokens=*" %%b in ('dir /B "%%a"') do if exist "%dest%\%%b" (
set "ext=%%~xa"
set "fname=%%~na"
if exist "%dest%\!fname!(!cnt!)!ext!" (set /a cnt=!cnt!+1)
set /a cnt=!cnt!+1
copy "%%a" "%dest%\!fname!(!cnt!)!ext!"
) else copy "%%a" "%dest%\%%b"
#echo off
setlocal
set "source=L:\an_2017\image_list.txt"
set "target=L:\an_2017"
for /f "delims=" %%A in (%source%) do (
if not exist "%target%\%%~nxA" (
copy "%%~A" "%target%\%%~nxA"
) else (
call :index "%%~A" "%target%\%%~nxA" "1"
)
)
exit /b
:index source, target, count
setlocal
set /a "cnt=%~3"
if exist "%target%\%~n2(%cnt%)%~x2" (
call :index "%~1" "%~2" "%cnt%+1"
) else copy "%~1" "%target%\%~n2(%cnt%)%~x2"
The source file is read line by line.
If the file is not found in the target folder, then a copy done.
If the file is found in the target, then the label :index
is called with arguments of source, target and the count of 1.
In the called label, cnt is set the value of the count.
If the indexed file is not found in target, then a copy is done.
If found in target, calls the label again until the indexed file
is not found in target and a copy is done.
If you prefer using type, then replace
(%source%) with ('type "%source%"').
Note: The index integer is 1 based and does restart from 1
for indexing and increments until the filename is not
in the target.

batch for loop in file

I have a Batch script :
#echo off
setlocal enableDelayedExpansion
SET /P UserInput=Please Enter a Number:
SET /A number=UserInput
ECHO number=%number%
for %%i in (*.jpeg) do call :JPG %%~ni %%i
goto :end
:JPG
set str=%1
set /a str2=%str:_color=%
set /a newnamej=%str2%+%number%
echo %1 ==> I can see the problem with it
set lastnamej=%newnamej%_color.jpeg
ren %2 %lastnamej%
goto :eof
:end
The goal of this script is to take all file in a folder. They are all named after a number (1_color.jpeg, 2_color.jpeg, 3_color.jpeg,..) and I want to rename them with an additionnal number (if user input is 5, 1_color.jpeg will become 6_color.jpeg, and so on).
I have a problem with this script.
if I use a number such as 555, the first file will pass in the for loop 2 times.
(little example : 1_color.jpeg and 2_color.jpeg,
I use my script with 5 so 1_color.jpeg => 6_color.jpeg and 2_color.jpeg => 7_color.jpeg but then, 6_color.jpeg will be read again once, and will become 11_color.jpeg, so my result will be 11_color.jpeg and 7_color.jpeg).
Do someone know how to fix this issue?
Thanks for all!
The problem have two parts: the for %%i in (*.jpeg) ... command may be dinamically affected by the position that a renamed file will occupy in the whole file list, so some files may be renamed twice and, in certain particular cases with many files, up to three times.
The solution is to use a for /F %%i in ('dir /B *.jpeg') ... command instead, that first get the list of all files, and then start the renaming process.
Also, the rename must be done from last file to first one order, to avoid duplicate numbers.
However, in this case the use of for /F combined with "tokens=1* delims=_" option also allows to process the first underscore-separated number in the file names in a simpler way:
#echo off
setlocal EnableDelayedExpansion
SET /P number=Please Enter a Number:
ECHO number=%number%
for /F "tokens=1* delims=_" %%a in ('dir /O:-N /B *.jpeg') do (
set /A newNum=%%a+number
ren "%%a_%%b" "!newNum!_%%b"
)
User Aacini provided a nice solution in his answer, pointing out both issues at hand, namely the fact that for does not fully enumerate the directory in advance (see this thread: At which point does for or for /R enumerate the directory (tree)?) and the flaw in the logic concerning the sort order of the processed files.
However, there is still a problem derived from the purely (reverse-)alphabetic sort order of dir /B /O:-N *.jpeg, which can still cause collisions, as the following example illustrates:
9_color.jpeg
8_color.jpeg
7_color.jpeg
6_color.jpeg
5_color.jpeg
4_color.jpeg
3_color.jpeg
2_color.jpeg
10_color.jpeg
1_color.jpeg
So if the entered number was 1, file 9_color.jpeg is tried to be renamed to 10_color.jpeg, which fails because that file already exists as it has not yet been processed (hence renamed to 11_color.jpeg).
To overcome this problem, you need to correctly sort the items in reverse alpha-numeric order. This can be achieved by left-zero-padding the numbers before sorting them, because then, alphabetic and alpha-numeric sort orders match. Here is a possible implementation:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_LOCATION=." & rem // (directory containing the files to rename)
set "_PATTERN=*_*.jpeg" & rem // (search pattern for the files to rename)
set "_REGEX1=^[0-9][0-9]*_[^_].*\.jpeg$" & rem // (`findstr` filter expression)
set "_TEMPFILE=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (path to temporary file)
rem // Retrieve numeric user input:
set "NUMBER="
set /P NUMBER="Please Enter a number: "
set /A "NUMBER+=0"
if %NUMBER% GTR 0 (set "ORDER=/R") else if %NUMBER% LSS 0 (set "ORDER=") else exit /B
rem /* Write `|`-separated list of left-zero-padded file prefixes, original and new
rem file names into temporary file: */
> "%_TEMPFILE%" (
for /F "tokens=1* delims=_" %%E in ('
dir /B "%_LOCATION%\%_PATTERN%" ^| findstr /I /R /C:"%_REGEX1%"
') do (
set "NAME=%%F"
setlocal EnableDelayedExpansion
set "PADDED=0000000000%%E"
set /A "NUMBER+=%%E"
echo !PADDED:~-10!^|%%E_!NAME!^|!NUMBER!_!NAME!
endlocal
)
)
rem /* Read `|`-separated list from temporary file, sort it by the left-zero-padded
rem prefixes, extract original and new file names and perform actual renaming: */
< "%_TEMPFILE%" (
for /F "tokens=2* delims=|" %%K in ('sort %ORDER%') do (
ECHO ren "%%K" "%%L"
)
)
rem // Clean up temporary file:
del "%_TEMPFILE%"
endlocal
exit /B
After having successfully verified the correct output of the script, to not forget to remove the upper-case ECHO command in front of the ren command line.
The script uses a temporary file that receives a |-separated table with the padded numeric prefix in the first, the original file name in the second and the new file name in the third column, like this:
0000000010|10_color.jpeg|11_color.jpeg
0000000001|1_color.jpeg|2_color.jpeg
0000000002|2_color.jpeg|3_color.jpeg
0000000003|3_color.jpeg|4_color.jpeg
0000000004|4_color.jpeg|5_color.jpeg
0000000005|5_color.jpeg|6_color.jpeg
0000000006|6_color.jpeg|7_color.jpeg
0000000007|7_color.jpeg|8_color.jpeg
0000000008|8_color.jpeg|9_color.jpeg
0000000009|9_color.jpeg|10_color.jpeg
The temporary file is read and sorted by the sort command. The strings from the second and third columns are extracted and passed over to the ren command.

In a batch file ran from Windows, select latest application version using directory name

I use a portable application that have updates quite often. The problem is that each version of the application has a folder named "processing-x.y.z". Each time I install a new version, I need to associate the files with the new version which is in a different folder. So to workaround this annoyance, I want to associate the "*.pde" file type to a batch file.
The folder names go as follow
processing-3.2.1
processing-3.2.2
etc.
I have created this small batch script that get the executable from the latest version.
#echo off
for /f "delims=" %%D in ('dir processing* /a:d /b /o-n') do (
set currentFolder=%%~fD
:: Check if environment variable already set
if not %currentFolder%==%processing% (
:: Set environment variable processing
setx processing %currentFolder%
)
%currentFolder%/processing.exe %1
goto :eof
)
It works when launching it from the command-line, but not within Windows. Is there a specific reason? Also, is there a way to optimize this code?
Thanks
Supposing the version numbers always consist of a single digit each, I would do it the following way:
#echo off
rem // Reset variable:
set "currentFolder="
rem /* Loop through the folders in ascending order, overwrite the variable
rem in each iteration, so it holds the highest version finally: */
for /f "delims=" %%D in ('dir /B /A:D /O:N "processing-*.*.*"') do (
set "currentFolder=%%~fD"
)
rem // Check if environment variable is already set:
if not "%processing%"=="%currentFolder%" (
rem // Set environment variable `processing`:
setx processing "%currentFolder%"
)
rem // Execute `processing.exe`:
"%currentFolder%/processing.exe" "%~1"
If the individual version numbers can consist of more than one digit (four at most here), use this:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem /* Assign each found folder to a variable called `$ARRAY_X_Y_Z`, where `X`, `Y`, `Z`
rem are zero-padded variants of the original numbers `x`, `y`, `z`, so for instance,
rem a folder called `processing-4.7.12` is stored in variable `$ARRAY_0004_0007_0012`: */
for /F "tokens=1-4 delims=-. eol=." %%A in ('
dir /B /A:D "processing-*.*.*" ^| ^
findstr /R /I "^processing-[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*$"
') do (
rem // Perform the left-side zero-padding here:
set "MAJ=0000%%B" & set "MIN=0000%%C" & set "REV=0000%%D"
set "$ARRAY_!MAJ:~-4!_!MIN:~-4!_!REV:~-4!=%%A-%%B.%%C.%%D"
)
rem // Reset variable:
set "currentFolder="
rem /* Loop through the output of `set "$ARRAY_"`, which returns all variables beginning
rem with `$ARRAY_` in ascending alphabetic order; because of the zero-padding, where
rem alphabetic and alpha-numeric orders become equivalent, the item with the greatest
rem version number item is iterated lastly, therefore the latest version is returned: */
for /F "tokens=1,* delims==" %%E in ('set "$ARRAY_"') do (
set "currentFolder=%%F"
)
endlocal & set "currentFolder=%currentFolder%"
rem // The rest of the script os the same as above...
You can also find similar approaches here:
How to get latest version number using batch (this approach also relies on the sorting featurre of the set command)
How to sort lines of a text file containing version numbers in format major.minor.build.revision numerical? (this uses the sort command upon a temporary file or on piped (|) data)
not tested (edited: should handle the cases when minor versions have more digits):
#echo off
setlocal enableDelayedExpansion
set /a latest_n1=0
set /a latest_n2=0
set /a latest_n3=0
for /f "tokens=2,3* delims=-." %%a in ('dir processing* /a:d /b /o-n') do (
set "current_v=%%a.%%b.%%c"
set /a "current_n1=%%a"
set /a "current_n2=%%b"
set /a "current_n3=%%c"
if !current_n1! GTR !latest_n1! (
set /a latest_n1=!current_n1!
set /a latest_n2=!current_n2!
set /a latest_n3=!current_n3!
set "latest_v=!current_v!"
) else if !current_n1! EQU !latest_n1! if !current_n2! GTR !latest_n2! (
set /a latest_n1=!current_n1!
set /a latest_n2=!current_n2!
set /a latest_n3=!current_n3!
set "latest_v=!current_v!"
) else if !current_n1! EQU !latest_n1! if !current_n2! EQU !latest_n2! if !current_n3! GTR !latest_n3! (
set /a latest_n1=!current_n1!
set /a latest_n2=!current_n2!
set /a latest_n3=!current_n3!
set "latest_v=!current_v!"
)
)
echo latest version=processing-%latest_v%
#echo off
for /f "delims=" %%D in ('dir processing* /a:d /b /o-n') do (
if "%processing%" neq "%cd%\%%F" setx processing "%cd%\%%F" >nul
.\%%F\processing.exe %1
goto :eof
)
is equivalent code - almost.
First problem - the :: form of comments should not be used within a code block (parenthesised series of statements) as :: is in fact a label that itself stars with a colon. Since it is a label, it terminates the block.
Next problem - within a code block, %var% refers to the value ofvar**as it stood when thefor` was encountered** - not as it changes within the loop.
Next problem - as noted by others, /o-n produces a by-name sequence, so 10 is likely to sort after 9 (since the sort is reversed). I've not changed this in the replacement code, but /o-d to sort by reverse-date may be better suited to your application.
Now to how your code works.
First, processing is set to whatever the last run established. If that is different from the value calculated from this run, then setx the new value. Bizarrely, setx does not set the value in the current cmd instance, only for instances created in the future.
You then attempt to execute your process and then exit the batch with the goto :eof. Only problem is that %currentfolder% is not the value as changed by the loop, because it's supposed to be in the code block. It appears to change because the ::-comments have broken the block and where currentfolder is used in your code, it is outside of the block.
Instead, use .\%%F\executablename which means "from the current directoryname (.);subdirectory %%F;filenametoexecute" - note that "\" is a directory-separator in windows, '/' indicates a switch.

batch script to delete every other file in directories

I have a directory with many sub-directories that contain thousands of jpgs. What I want to do is create a batch script that will go through all of the sub-directories and delete every 2nd image i.e. keep the first, third, fifth image but delete the second, fourth, and six image etc per directory (ordered by filename).
I tried with the following but my knowledge of batch scripting is poor, and the syntax is clearly incorrect.
#echo off
set z = 0
for /f %%a in ('dir/b *.jpg')
do (
set z = z + 1
if z == 2 del %%a
)
The DO must be on the same line as FOR.
You must use SET /A if you want to do math
Your logic is wrong - Currently it will only delete the 2nd file, not every other one. You should take the mod 2 value (remainder devided by 2) and delete if the result is 0.
You must use %z% if you want to see the current value (except within a SET /A statement). But that will not work inside a code block that just set the value. In that case you need to enable delayed expansion and use !z! instead.
Expanding a FOR variable that contains ! (valid in file names) while delayed expansion is enabled will corrupt the value. So delayed expansion must be toggled on and off
You say you want to recurse sub-directories, but your code only looks at one folder.
Spaces are significant in the SET statement. Your code defines a variable z with a space at the end of the name. Not what you want.
Here is a debugged version:
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do (
set /a "z+=1, r=z%%2"
setlocal enableDelayedExpansion
if !r! equ 0 del "%%D\%%F"
endlocal
)
)
There are ways to solve this without delayed expansion. One is to simply alternate between defining and undefining a variable.
#echo off
setlocal
for /r %%D in (.) do (
set "del="
for /f %%F in ('dir /b "%%D\*.jpg"') do if defined del (
del "%%D\%%F"
set "del="
) else set "del=1"
)
Another is to intentionally divide by 0 when you want to delete, and delete only when there is an error. Error messages are hidden by 2>nul, and the || operator conditionally executes the following command only if the prior command failed.
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do 2>nul set /a "z+=1, 1/(z%%2)" || del "%%D\%%F"
)
try this and remove the echo if the output looks good:
#echo off &setlocal
for /f "tokens=1*delims=:" %%a in ('dir /b /s /a-d *.jpg^|findstr /n $') do (
echo %%a|findstr "[02468]$" >nul && echo del "%%~b"
)

Resources