1

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

NickB
  • 349
  • 4
  • 15
  • 1
    1. This approach works only as long as the version numbers are one digit each; as soon as you have e. g. `3.2.10`, sorting fails. 2. Since you are setting *and* reading variables within the same block of code, you need [delayed expansion](http://ss64.com/nt/delayedexpansion.html); alternatively, revert the sort order (`/on`) and keep `set currentFolder=%%~fD` as the only code within the `for /F` loop, then do all the rest outside of the loop... – aschipfl Dec 07 '16 at 18:24
  • @aschipfl - is this comment aimed for my answer? :-D – npocmaka Dec 07 '16 at 18:26
  • @npocmaka, I guess you posted it while I was typing my comment, so no... – aschipfl Dec 07 '16 at 18:28

3 Answers3

1

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:

Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99
0

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%
npocmaka
  • 55,367
  • 18
  • 148
  • 187
0
@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.

Magoo
  • 77,302
  • 8
  • 62
  • 84