0

I'm trying to write a batch file that will look in a directory containing multiple folders with sequential project numbers and arbiary names, such as:

0001 - ProjectA
0002 - ProjectB
.....
0009 - ProjectX
0010 - ProjectY 

and calculate the next project number (with the aim of creating a new folder with the next project number in it). Here's what i've got so far:

cls

setlocal 

cd /d c:\Test

for /f "tokens=1 delims= " %%1 in ('dir /b /ad /on') do (set latestdir=%%1)

echo %latestdir%

set /a nextdir=%latestdir% + 1

echo %nextdir% 

pause

I've noticed that this method falls down if the number of preceeding 0's varies. I.e. change 0010 with 00010 and 004 is found as the last folder. I've tried many things and snippets of code found on the web to remove preceeding 0's and negate the effect, but with no luck.

This directory will also contain non-project folders that don't start with a number.

Any pointers would be really appreciated!

Smartbuild
  • 119
  • 3
  • 15
  • Just to say thanks to both aschipfl and Squashman for their answers. 'Answer' allocated to aschipfl just because it was simpler for me to implement (all contained in a single batch) as well as the depth of explanation and follow up. If I could award answer to both I would! – Smartbuild Dec 18 '15 at 20:21

3 Answers3

2
@echo off
setlocal

rem Get last folder that start with digit (i.e. 0010)
cd /d c:\Test
for /L %%i in (0,1,9) do (
   for /f "tokens=1 delims= " %%a in ('dir /b /ad /on %%i*') do set "latestdir=%%a"
)
echo %latestdir%

rem Get next number preceded by 1 (i.e. 10010+1=10011)
set /a nextdir=1%latestdir%+1

rem Show the number removing first digit (i.e. 0011)
echo %nextdir:~1%

EDIT: If the number may have a variable number of digits and you want to force a 4-digits output, then you may use this method instead:

rem Show the last four digits in number (i.e. 0011)
echo %nextdir:~-4%

However, this method will fail if the number have less than 4 digits...

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Hi Aacini, this seems to fail when we use the users example of folder name with an extra zero: 00010. I also tested it with folder names of 20000 and 9000 and it returned 9000 as the largest. Not sure if that last scenario is really within the scope the user wanted. – Squashman Dec 18 '15 at 20:30
  • @Squashman: Yes. From the question I assumed the numbers always have 4 digits. I added a modification in the method to manage numbers with more than 4 digits, but the method will fail if the number have _less_ digits (we need a more precise specification of the problem). – Aacini Dec 18 '15 at 20:38
  • I was coding it based on his **00010** example and the question title: **Find next folder number with variable preceding zeros**. So I assumed it could possibly be any number: 001, 0010, 0100,1000,999,098,20000 – Squashman Dec 18 '15 at 20:42
  • And if you didn't notice, my last set of code was something I stole from you and modified it a bit. http://www.dostips.com/forum/viewtopic.php?t=5583#p34343 What did Steve Jobs say........ – Squashman Dec 18 '15 at 20:44
1

We have a batch file called SORTN over on Dostips.com that helps with numeric sorting. I am posting that code here for posterity.

@ECHO OFF
if "%~1"=="/?" (
    echo.Sorts text by handling first number in line as number not text
    echo.
    echo.%~n0 [n]
    echo.
    echo.  n     Specifies the character number, n, to
    echo.        begin each comparison.  3 indicates that
    echo.        each comparison should begin at the 3rd
    echo.        character in each line.  Lines with fewer
    echo.        than n characters collate before other lines.
    echo.        By default comparisons start at the first
    echo.        character in each line.
    echo.
    echo.Description:
    echo.        'abc10def3' is bigger than 'abc9def4' because
    echo.        first number in first string is 10
    echo.        first number in second string is 9
    echo.        whereas normal text compare returns 
    echo.        'abc10def3' smaller than 'abc9def4'
    echo.
    echo.Example:
    echo.        To sort a directory pipe the output of the dir
    echo.        command into %~n0 like this:
    echo.           dir /b^|%~n0
    echo.
    echo.Source: http://www.dostips.com
    goto:EOF
)
if "%~1" NEQ "~" (
    for /f "tokens=1,* delims=," %%a in ('"%~f0 ~ %*|sort"') do echo.%%b
    goto:EOF
)
SETLOCAL ENABLEDELAYEDEXPANSION
set /a n=%~2+0
for /f "tokens=1,* delims=]" %%A in ('"find /n /v """') do (
    set f=,%%B
    (
        set f0=!f:~0,%n%!
        set f0=!f0:~1!
        rem call call set f=,%%%%f:*%%f0%%=%%%%    
        set f=,!f:~%n%!
    )
    for /f "delims=1234567890" %%b in ("!f!") do (
        set f1=%%b
        set f1=!f1:~1!
        call set f=0%%f:*%%b=%%
    )
    for /f "delims=abcdefghijklmnopqrstuwwxyzABCDEFGHIJKLMNOPQRSTUWWXYZ~`@#$*_-+=:;',.?/\ " %%b in ("!f!") do (
        set f2=00000000000000000000%%b
        set f2=!f2:~-20!
        call set f=%%f:*%%b=%%
    )
    echo.!f1!!f2!!f!,%%B
    rem echo.-!f0!*!f1!*!f2!*!f!*%%a>&2
)

So here is the output when using the DIR command and piping it to SORTN and also the output of the normal DIR command.

C:\BatchFiles\SORTN>dir /ad /b 00* |sortn.bat
0001 - ProjectA
0002 - ProjectB
0004 - ProjectC
00010 - ProjectY

C:\BatchFiles\SORTN>dir /ad /b 00*
0001 - ProjectA
00010 - ProjectY
0002 - ProjectB
0004 - ProjectC

Insert MY DIR code into your FOR /F command. Updated to Strip the leading zeros.

@echo off
for /f "tokens=1 delims= " %%G in ('dir /b /ad 0* ^|sortn.bat') do set latestdir=%%G

:STRIP0
IF "%latestdir:~0,1%"=="0" (
    SET latestdir=%latestdir:~1%
    GOTO STRIP0
)
echo %latestdir%
pause

Adding one more option that does not use SORTN.BAT

@echo off
setlocal EnableDelayedExpansion
set "latestdir="
for /D %%j in (0* 1* 2* 3* 4* 5* 6* 7* 8* 9*) do (
   FOR /F "tokens=1 delims= " %%G IN ("%%j") DO set num=%%G
   set j=0000000!num!
   set name[!j:~-8!]=%%~nxj
)
for /F "tokens=2 delims== " %%j in ('set name[') do set latestdir=%%j

:STRIP0
IF "%latestdir:~0,1%"=="0" (
    SET latestdir=%latestdir:~1%
    GOTO STRIP0
)
echo %latestdir%

pause
Squashman
  • 13,649
  • 5
  • 27
  • 36
  • Thanks for the quick reply Squashman, but I'm not sure how to utilise this for my needs? I'm new to batchfiles and generally a fairly novice coder so excuse my ignorance! Basically, for the example folders I quoted, I need to a robust method that would give me a variable with the value '11', regardless of preceding 0 arrangements. Once I can do that I'm fairly sure I can do the rest. Thanks again. – Smartbuild Dec 18 '15 at 18:03
  • @user2048265 Updated to strip the leading zeros. – Squashman Dec 18 '15 at 18:24
  • @user2048265, if you think your folders are going to begin with a number other than a zero you can change the DIR command to use multiple masks. `dir /b /ad 0* 1* 2* 3* 4* 5* 6* 7* 8* 9*` – Squashman Dec 18 '15 at 18:34
  • @user2048265 I added one more set of code that does not use SORTN.bat. This will make it self contained. – Squashman Dec 18 '15 at 18:58
  • Thanks Squashman, with your and aschipfl's answers I'm pretty sure I've it covered. Really appreciate you taking time to update your answer. – Smartbuild Dec 18 '15 at 20:13
0

Here is the fixed code to retrieve the lastest (greatest) number in the directories and to return the next number:

@echo off
cls
setlocal EnableExtensions DisableDelayedExpansion
cd /D "C:\Test"
for /F "tokens=1 delims= " %%D in ('
    dir /B /A:D /O:-N "???? - *"
') do (
    set "latestdir=%%~D"
    goto :NEXT
)
:NEXT
for /F "tokens=* delims=0" %%N in ("%latestdir%") do (
    set "latestdir=%%~N"
)
echo "%latestdir%"
set /A "nextdir=latestdir + 1"
set "nextdir=000%nextdir%"
set "nextdir=%nextdir:~-4%"
echo "%nextdir%"
pause
endlocal

What I did:

  • the pattern ???? - * has been added to dir, so only such directories are returned: 4 characters, followed by SPACE-SPACE, followed by anything; this does not check whether or not the 4 leading characters are numeric digits though;
    • if you do want to check the first 4 digits for being purely numeric, append ^| findstr /R "^[0-9][0-9][0-9][0-9]" to the dir command line (the ^| is only necessary when this is used within for /F as in the above code; when used stand-alone, just state |);
  • the sort order of dir has been inverted (/O:-N), so the latest directory is returned first; the goto breaks the for /F loop, so only the first directory is processed; this might improve the overall performance;
  • a second for /F loop has been added, which removes all leading 0 from %latestdir%; this is necessary because set /A treats numbers with leading zeros as octal numbers, which may lead to unexpected results;
  • after set /A, which does the increment by 1 to get the next directory index (and does not fail even in case the latest number is empty meaning zero, due to the preceding for /F loop), two more set command lines have been inserted:
    1. the first one precedes 000 to the number %nextdir%;
    2. the next one uses the substring expansion syntax and returns the last 4 digits;
  • in general, the set syntax has been improved, surrounding "" have been added;

Note:
The sort order of the directories relies on the fact that the numeric prefixes all consist of the same number of digits, as dir is not capable of true alphanumeric sorting, it merely sorts alphabetically (or more technically said, by the character codes).


Edit:

Here is a solution that does no longer persist on the numbers to consist of exactly 4 digits, so they can consist of up to 12 digits now. Since dir /O does an alphabetic sort rather than an alphanumeric one, the /O switch has been removed. The retrieved numbers are padded with zeros to consist of 12 digits each, then set is used to actually sort, also in an alphabetic manner, but because of the padding this provides exactly the same result as alphanumeric sorting. To use set for sorting, I utilise the fact that it returns a sorted list of environment variables if only a variable name prefix is given. Here I am using currentdir_ as prefix, after I have created a variable per iterated item whose name is currentdir_ with the padded numeric part appended. As value the pure numeric value without any leading zeros is stored. When parsing the sorted list returned by set, each item is split at the = character which is followed by the said numeric value. The last one of those constitutes the greatest one and is then used to calculate the next number:

@echo off
cls
setlocal EnableExtensions DisableDelayedExpansion
cd /D "C:\Test"
for /F "tokens=1 delims= " %%D in ('
    dir /B /A:D "* - *" ^| findstr /R /C:"^[0-9][0-9]* - "
') do (
    set "currentdir=00000000000%%~D"
    for /F "tokens=* delims=0" %%Z in ("%%~D") do (
        if "%%~Z"=="" (
            call set "currentdir_%%currentdir:~-12%%=0"
        ) else (
            call set "currentdir_%%currentdir:~-12%%=%%~Z"
        )
    )
)
for /F "tokens=2 delims==" %%N in ('set "currentdir_"') do (
    set "latestdir=%%~N"
)
echo "%latestdir%"
set /A "nextdir=latestdir + 1"
set "nextdir=000%nextdir%"
set "nextdir=%nextdir:~-4%"
echo "%nextdir%"
pause
endlocal
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Thankyou aschipfl! I'll take the time to fully understand your code (explanation really appreciated) but from initial reading I'm SO glad you explained the octal number issue - when testing other variations of the code I was getting issues when folders with the number 8 or 9 was present - googling made me aware of something about octal but I had no idea how to get around it! I've not tested it yet but all makes sense, so thanks again! One other question - does the '????' pattern limit me to using numbers only in a #### formate (4 numbers), or in other words, what does it do?! – Smartbuild Dec 18 '15 at 18:10
  • Ahh, apologies, just read your 'Note' regarding no. of digits, my above comment question therefore redundant. I guess I was wondering if the folder names could have preceeding 0's removed, and then be put in an array which is then sorted to determine max number. Perhaps this is beyond the scope of batchfiles? – Smartbuild Dec 18 '15 at 18:17
  • The `?` wildcard stands for _any single_ character, the `*` means _zero or more_ characters; there are no other wildcards available unfortunately; see also my updated answer... – aschipfl Dec 18 '15 at 18:20
  • 1. to check whether the 4 digits at the beginning are purely numeric, `dir /B /A:D /O:-N "???? - *" | findstr /R "^[0-9][0-9][0-9][0-9]"` could be used instead of the current `dir` command line (replace `|` by `^|` to use that within `for /F`); 2. for numerical sorting, I would go the other way and pad as many zeros as necessary so that all numbers have the same number of digits; then numerical and text sorting lead to the same result, hence the native `sort` command can be used... – aschipfl Dec 18 '15 at 18:41
  • ...concerning **alphanumeric sorting**, you might be interested in [this answer](http://stackoverflow.com/a/34056239/5047996) I posted recently... – aschipfl Dec 18 '15 at 19:03
  • thankyou aschipfl for your continued input, that post looks like it could useful too - lots for me to learn! I agree with the zero padding, mainly because I like the consistency of it, however my users (and me) like to type the project number as a quick way to get to the folder and, thinking about it now, the additional zeros would make that harder. So perhaps I over complicated things - if there are no leading zeros it all becomes simpler! At least I have options and am better informed now! Thanks again. – Smartbuild Dec 18 '15 at 20:11
  • You're welcome! concerning padding: I meant to pad with zeros just before the list is passed to the sorting tool, but not to actually rename the directories... I have an idea how this could be done -- I will add it to my answer as soon as I have it done... – aschipfl Dec 18 '15 at 20:30
  • @user2048265, as announced I added a solution that covers the situation that the prefix numbers vary in lengths -- see my [edited answer](http://stackoverflow.com/revisions/34361486/6)... – aschipfl Dec 28 '15 at 22:05