In my batch file I have the following variables:
set collection=collection1
set environment=oracleDev
set processChain1=-help,-startimport %environment% %collection%
As you can see, my process chain contains two strings that are separated with a ",".
Now I want to count the two strings (later it could be more then one string). I tried it with:
Set count=0
For %%j in (%%processChain1%%) Do Set /A count+=1
echo %count%
But there is the first mistake. It prints out 1 and not 2. Why?
After counting the strings I want to start an application with each parameter (string from the variable processChain1)
I try it with:
FOR /L %%G IN (1,1,%count%) DO (
FOR /F "tokens=%count% delims=," %%H IN ("%processChain1%") DO java -jar App.jar %%H
)
This cant work correct now because the counter is wrong because of the first mistake. But I think if I can solve the first problem, the second should work fine. Is this correct?
As far I can tell, right now, is counting 1 because there's only one string in that var, you are making the split later, but your token count is already set to 1....
You need to split the first string (delims=,) and then in the second part, work with each result.
EDITED:
Try this...
#echo off
set collection=collection1
set environment=oracleDev
set processChain1="-help" "-startimport %environment% %collection%"
Set count=0
For %%j in (%processChain1%) Do Set /A count+=1
echo.Total count: %count%
pause
As you can see, I change the var processChain1 structure to separate the values with a space (default delimeter) and put every var in quotes...
At least it works, and gives you the total count.
Only of course, If you can use it in this way.
Hope it helps.
Cheers.
If not.. take a look here, maybe it's help : separate tokens in batch file
Good luck
EDITED 2 (to match new information)
Batch file: Metalhead89.bat
#echo off
:: define the vars
set collection=collection1
set environment=oracleDev
:: concatenate the vars with ++
set processChain1=-help -startimport++%environment%++%collection%
:: Get the total count plus, run each token found
Set count=0
For %%j in (%processChain1%) do (
Set /A count+=1
set line=%%j
call :processToken
)
:: This will be printed out, at the end of the loop
echo Total token count: %count%
goto :eof
:processToken
for /f %%f in ("%line%") do (
:: set the command var with your exe file for each token found
set command=Running command: java -jar app.jar %%f
call :runTheCommand
)
goto :eof
:runTheCommand
:: now we replace the doble ++ in the var string with space, to treat them as options
set str=%command%
set str=%str:++= %
:: finally we do a echo of the command with the option included
echo %str%
goto :eof
Now, Call that file from command line and you will get:
Z:\>call Metalhead89.bat
Running command: java -jar app.jar -help
Running command: java -jar app.jar -startimport oracleDev collection1
Total token count: 2
Good luck buddy ;-)
Here I am going to present my solution but the solution of gmo is at least as good as mine.
#echo off
rem !!!!!!!!!!!!!!!!!!!!!
rem Set Parameter options
rem !!!!!!!!!!!!!!!!!!!!!
set collection=collection1
set environment=oracleDev
rem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
rem Set Modules As Parameters
rem Watch out: Each module + his options has to be in quotation marks
rem Options are separated by comma without whitespaces
rem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
set helpModul="-help"
set importModul="-startimport,%environment%,%collection%"
rem !!!!!!!!!!!!!!!!!!!!!!!
rem Configure Process Chain
rem !!!!!!!!!!!!!!!!!!!!!!!
set activeProcessChain=%helpModule%,%importModul%
rem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
rem Start Content Integration Testing Framework
rem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Set count=0
For %%j in (%activeProcessChain%) Do Set /A count+=1
FOR /L %%H IN (1,1,%COUNT%) DO (
call :loopThroughParams %%H
)
exit /b
:loopThroughParams
FOR /F "tokens=%1 delims=," %%I IN ("%activeProcessChain%") Do (
echo.
echo.
java -jar %nameOfApplication% %%~I
)
exit /b
:end
Related
I want a batch file which will find out which is the second latest folder created/modified in a directory.
I found this article but no matter how much i tried i could not understand how it works
#echo off
set "root_dir=c:\somewhere"
pushd "%root_dir%"
set "bl1="
set "bl2="
setlocal enableDelayedExpansion
for /f "tokens=* delims=" %%# in ('dir /b /a:-d /o:d') do (
set "bl2=!bl1!"
set "bl1=%%#"
)
echo %bl2%
endlocal
If i use it as it is then i can get the second latest folder but this script is supposedly able to get which ever latest folder you need , be it 1st or nth.
Could someone please tell me what modifications need to be done to the script to accomplish that. Also how exactly this script works
In your approach, the latest folder is already available in variable bl1; add echo %bl1% at the end before endlocal to display it. Retrieving the nth folder is simply not possible in a flexible way with that script as you would need to define another variable (say bl3, bl4,..., bln) within the loop.
However, you could reverse the sort order of the output of the dir command by changing the /O option, so it returns the latest (most recent) item first. Then let an index number count the iterations of the loop, and if that index equals the predefined number n, store the currently iterated item:
#echo off
setlocal EnableDelayedExpansion
rem // Define N here to get Nth-latest folder:
set /A LATEST=2
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "C:\somewhere"') do (
set /A INDEX+=1
if !INDEX! EQU !LATEST! (
set "ITEM=%%#"
)
)
if defined ITEM echo %LATEST%th-latest folder: %ITEM%
endlocal
exit /B
Update
Here is a modified script with the following improvements:
Exclamation marks ! in folder names are no longer lost due to toggling delayed expansion;
the target directory can be provided as the first command line argument; if omitted, the current directory is used;
the number n can be given as the second command line argument; if omitted, the user is prompted for it (this addresses elzooilogico's comment); n defaults to 1 for empty input;
the display output is improved to avoid something weird like 1th-latest, 2th-latest and 3th-latest; instead, The latest, 2nd-latest and 3rd-latest is returned, respectively;
So this is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* define location path and folder pattern as 1st command line argument;
rem /* Define number N as 2nd command line argument to get Nth-latest folder. */
set "LATEST=%~2"
set /A LATEST+=0
if %LATEST% LEQ 0 (set /P LATEST="Enter N [1]: " & set /A LATEST+=0)
if %LATEST% LEQ 0 set /A LATEST=1
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "%~1"') do (
set /A INDEX+=1
setlocal EnableDelayedExpansion
if !INDEX! EQU !LATEST! (
endlocal
set "ITEM=%%#"
goto :SKIP & rem // break loop after having retrieved Nth-latest folder;
) else endlocal
)
:SKIP
setlocal EnableDelayedExpansion
if defined ITEM (
if %LATEST% EQU 1 (echo The latest file: !ITEM!) else (
if %LATEST% EQU 2 (echo 2nd-latest file: !ITEM!) else (
if %LATEST% EQU 3 (echo 3rd-latest file: !ITEM!) else (
echo %LATEST%th-latest file: !ITEM!)))
)
endlocal
endlocal
exit /B
To achieve a similar result as with the simple script on top of this answer, you need to call this script by the following command line, supposing it has been saved as Nth-latest.bat:
Nth-latest.bat "C:\somewhere" 2
I have a txt file with values like this:
3.6.4.2
3.6.5.1
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.5.2
3.6.7.1
3.6.7.10
3.6.7.11
3.6.7.2
3.6.7.3
I need to write a batch script and return a sorted output. The problem is with last column, numbers .10 and .11 should go after .3 and so. I need the "latest version" to be on the bottom, which in this case is 3.6.7.11
In Linux I used "sort -t"." -k1n,1 -k2n,2 -k3n,3 -k4n,4" but I can't get it working with batch script.
Also I am not allowed to use Cygwin or PowerShell for some reasons.
In my batch code I am so far trying only various versions of this but nothing is working for me:
sort /+n versions.txt
The output used in this question is simply
sort versions.txt
It looks like that command sort is doing it correctly until I don't have 2 digits number used.
This is a common problem in Batch files. All sorting methods use a string comparison, where "10" comes before "2", so it is necessary to insert left zeros in the numbers less than 10. The Batch file below do that, but instead of generate a new file with the fixed numbers, it uses they to create an array that will be automatically sorted. After that, the array elements are shown in its natural (sorted) order.
EDIT: I modified the code in order to manage two digits numbers in the four parts.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=." %%a in (input.txt) do (
rem Patch the four numbers as a two digits ones
set /A "a=100+%%a, b=100+%%b, c=100+%%c, d=100+%%d"
rem Store line in the proper array element
set "line[!a:~1!!b:~1!!c:~1!!d:~1!]=%%a.%%b.%%c.%%d"
)
rem Show array elements
for /F "tokens=2 delims==" %%a in ('set line[') do echo %%a
Output:
3.6.4.2
3.6.5.1
3.6.5.2
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.7.1
3.6.7.2
3.6.7.3
3.6.7.10
3.6.7.11
Based on your example this will work. If you should somehow end up with examples like 3.6.5.02 and 3.6.5.2, then this code will not work.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=. " %%G in (FILE.TXT) do (
set N=0%%J
set SORT[%%G%%H%%I!N:~-2!]=%%G.%%H.%%I.%%J
)
for /F "tokens=2 delims==" %%N in ('set SORT[') do echo %%N
pause
Here is my solution working with 2 temporary files which works also if one of the other 3 version numbers becomes ever greater than 9.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "VersionsFile=versions.txt"
rem Delete all temporary files perhaps existing from a previous
rem run if user of batch file has broken last batch processing.
if exist "%TEMP%\%~n0_?.tmp" del "%TEMP%\%~n0_?.tmp"
rem Insert a leading 0 before each number in version string if the
rem number is smaller than 10. And insert additionally a period at
rem start of each line. The new lines are written to a temporary file.
for /F "useback tokens=1-4 delims=." %%A in ("%VersionsFile%") do (
if %%A LSS 10 ( set "Line=.0%%A." ) else ( set "Line=.%%A." )
if %%B LSS 10 ( set "Line=!Line!0%%B." ) else ( set "Line=!Line!%%B." )
if %%C LSS 10 ( set "Line=!Line!0%%C." ) else ( set "Line=!Line!%%C." )
if %%D LSS 10 ( set "Line=!Line!0%%D" ) else ( set "Line=!Line!%%D" )
echo !Line!>>"%TEMP%\%~n0_1.tmp"
)
rem Sort this temporary file with output written to one more temporary file.
rem The output could be also printed to __stdout__ and directly processed.
%SystemRoot%\System32\sort.exe "%TEMP%\%~n0_1.tmp" /O "%TEMP%\%~n0_2.tmp"
rem Delete the versions file before creating new with sorted lines.
del "%VersionsFile%"
rem Read sorted lines, remove all leading zeros after a period and also
rem the period inserted at start of each line making it easier to remove
rem all leading zeros. The lines are written back to the versions file.
for /F "useback delims=" %%L in ("%TEMP%\%~n0_2.tmp") do (
set "Line=%%L"
set "Line=!Line:.0=.!"
set "Line=!Line:~1!"
echo !Line!>>"%VersionsFile%"
)
rem Finally delete the two temporary files used by this batch file.
del "%TEMP%\%~n0_?.tmp" >nul
endlocal
The first temporary file with unsorted lines contains for input example:
.03.06.04.02
.03.06.05.01
.03.06.05.10
.03.06.05.11
.03.06.05.12
.03.06.05.13
.03.06.05.02
.03.06.07.01
.03.06.07.10
.03.06.07.11
.03.06.07.02
.03.06.07.03
The second temporary file with the sorted lines contains for input example:
.03.06.04.02
.03.06.05.01
.03.06.05.02
.03.06.05.10
.03.06.05.11
.03.06.05.12
.03.06.05.13
.03.06.07.01
.03.06.07.02
.03.06.07.03
.03.06.07.10
.03.06.07.11
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~n0 (name of batch file without path and file extension)
del /?
echo /?
endlocal /?
for /?
if /?
rem /?
set /?
setlocal /?
sort /?
Easiest solution would be to invoke PowerShell and treat the version numbers as actual System.Version objects. That way the Major, Minor, Build, and Revision segments will be treated as integers and sorted accordingly. You can call this from a batch script:
powershell "(gc textfile.txt | %%{[version]$_} | sort) -split ' '"
That's it. Easy one-liner. If doing it from the cmd prompt, replace the double %% with a single %. Here's a breakdown of the command:
Get the following as a string:
Get the contents of textfile.txt
For each line, cast the data as a System.Version object.
Sort as versions
The string will be a single line separated by spaces. Split on the spaces.
Output is as follows:
3.6.4.2
3.6.5.1
3.6.5.2
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.7.1
3.6.7.2
3.6.7.3
3.6.7.10
3.6.7.11
Partial credit should go to this question and accepted answer.
In pure batch scripting, you could use the following code snippet:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
> "versions.tmp" (
for /F "usebackq tokens=1,2,3,4 delims=." %%I in ("versions.txt") do (
set "ITEM1=000%%I" & set "ITEM2=000%%J" & set "ITEM3=000%%K" & set "ITEM4=000%%L"
echo !ITEM1:~-4!.!ITEM2:~-4!.!ITEM3:~-4!.!ITEM4:~-4!^|%%I.%%J.%%K.%%L
)
)
< "versions.tmp" (
for /F "tokens=2 delims=|" %%S in ('sort') do (
echo %%S
)
)
del /Q "versions.tmp"
endlocal
exit /B
This creates a temporary file, which contains the original line, prefixed with padded version numbers and a separtor |. Padded numbers means that each component is padded with leading zeros to four digits. Here is an example based on youe sample data:
0003.0006.0004.0002|3.6.4.2
0003.0006.0005.0001|3.6.5.1
0003.0006.0005.0010|3.6.5.10
0003.0006.0005.0011|3.6.5.11
0003.0006.0005.0012|3.6.5.12
0003.0006.0005.0013|3.6.5.13
0003.0006.0005.0002|3.6.5.2
0003.0006.0007.0001|3.6.7.1
0003.0006.0007.0010|3.6.7.10
0003.0006.0007.0011|3.6.7.11
0003.0006.0007.0002|3.6.7.2
0003.0006.0007.0003|3.6.7.3
This temporary file is then passed over to sort which does a purely alphabetic sorting. Since the numbers are padded, the sort order equals the true alphanumeric order. Here is the sorting result using the above example:
0003.0006.0004.0002|3.6.4.2
0003.0006.0005.0001|3.6.5.1
0003.0006.0005.0002|3.6.5.2
0003.0006.0005.0010|3.6.5.10
0003.0006.0005.0011|3.6.5.11
0003.0006.0005.0012|3.6.5.12
0003.0006.0005.0013|3.6.5.13
0003.0006.0007.0001|3.6.7.1
0003.0006.0007.0002|3.6.7.2
0003.0006.0007.0003|3.6.7.3
0003.0006.0007.0010|3.6.7.10
0003.0006.0007.0011|3.6.7.11
Finally, if you want to return the latest version number only, echo %%S by set "LVER=%%S" and place echo !LVER! after the closing ) of the second for /F loop.
Update:
Here is a solution that does not produce any temporary files, but uses a pipe | instead. Since a pipe creates new cmd instances for both left and right sides, and due to the fact that the (console) outputs are built in tiny bits and that there are multiple arithmetic operations done, it is rather slow:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
(
for /F "usebackq tokens=1,2,3,4 delims=." %%I in ("versions.txt") do #(
set /A "10000+%%I" & echo( ^| set /P "=."
set /A "10000+%%J" & echo( ^| set /P "=."
set /A "10000+%%K" & echo( ^| set /P "=."
set /A "10000+%%L" & echo(
)
) | (
for /F "tokens=1,2,3,4 delims=." %%S in ('sort') do #(
set /A "%%S-10000" & echo( ^| set /P "=."
set /A "%%T-10000" & echo( ^| set /P "=."
set /A "%%U-10000" & echo( ^| set /P "=."
set /A "%%V-10000" & echo(
)
)
endlocal
exit /B
Left Side of the Pipe:
Instead of the substring expansion syntax like in the above approach using a temporary file, I add 10000 to every component of the version numbers (similar to Aacini's answer) in order to avoid delayed expansion, because this is not enabled in either new cmd instance. To output the resulting values, I make use of the fact that either of the for /F loops are running in cmd context rather than in batch context, where set /A outputs the result to STDOUT. set /A does not terminate its output with a line-break, so I use set /P to append a . after each but the last item, which in turn does not append a line-break. For the last item I append a line-break using a blank echo.
Right Side of the Pipe:
The sorting is again accomplished by the sort command, whose output is parsed by for /F. Here the previously added value 10000 is subtracted from each component to retrieve the original numbers. For outputting the result to the console, the same technique is used as for the other side of the pipe.
Piped Data:
The data passed over by the pipe looks like this (relying on the example of the question once again):
10003.10006.10004.10002
10003.10006.10005.10001
10003.10006.10005.10010
10003.10006.10005.10011
10003.10006.10005.10012
10003.10006.10005.10013
10003.10006.10005.10002
10003.10006.10007.10001
10003.10006.10007.10010
10003.10006.10007.10011
10003.10006.10007.10002
10003.10006.10007.10003
Back in DOS 5 (LOL) I thought I understood batch files, but I'm at a loss.
I have a series of files:
disc51.mp3
disc52.mp3
disc53.mp3
disc54.mp3
disc55.mp3
disc56.mp3
disc57.mp3
disc58.mp3
disc59.mp3
disc510.mp3
disc511.mp3
...etc
I need them to be renamed:
disc501.mp3
disc502.mp3
disc503.mp3
disc504.mp3
disc505.mp3
disc506.mp3
disc507.mp3
disc508.mp3
disc509.mp3
disc510.mp3
disc511.mp3
So I need to only rename the first 9 files in the sequence and do so by adding a '0' between char 5 and 6. How do I do this?
This is my first stab which assumes that each file will begin with 'disc5'. However, I'd like something more generic, ie. that would work properly regardless of the initial naming convention. The only thing I'd like to assume is that after that 'name' the numbers would be 1,2,3,4,5,6,7,8,9,10,11,12, etc.
echo off
cls
setlocal enabledelayedexpansion
FOR %%G IN (*.MP3) DO ( call :strlen %%G)
exit /b
)
:strlen
set myvar=%~n1
rem now compute the length of the string
set #=!myvar!
set length=0
:loop
if defined # (
rem shorten string by one character
set #=!#:~1!
rem increment the string count variable %length%
set /A length += 1
rem repeat until string is null
goto loop
)
rem assuming file name starts with disc. I'd like to make this a more general case
if %length%==6 ren %1 !myvar:~0,5!0!myvar:~5,1!.mp3
exit /b
#echo off
for /L %%i in (1,1,9) do ren "%1%%i.mp3" "%10%%i.mp3"
Previous Batch file requires the initial name in the first parameter, for example:
test.bat disc5
You may also use previous method directly in the command-line:
for /L %i in (1,1,9) do ren "disc5%i.mp3" "disc50%i.mp3"
I've got a batch script that creates a folder named New_Folder and a few subdirectories and files within. Currently, if I need to create multiple New_Folders I have to rename each New_Folder created by the batch before I can run it again and create a new one. What I'd like to do is have the batch check and see if New_Folder already exists, and if so, to increment New_Folder by a number. So I'd have New_Folder, New_Folder1, New_Folder2, and so on.
How would I go about doing this? The solutions I've seen for incrementing things in batch scripts don't seem to apply to my situation, and I don't know anything about batch scripting beyond what I've copy/pasted for my own code.
Here is a solution that will always work, even if there are gaps in the numbers. The folder number will always be 1 greater than the current max number.
#echo off
setlocal enableDelayedExpansion
set "baseName=New_Folder"
set "n=0"
for /f "delims=" %%F in (
'2^>nul dir /b /ad "%baseName%*."^|findstr /xri "%baseName%[0-9]*"'
) do (
set "name=%%F"
set "name=!name:*%baseName%=!"
if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%baseName%%n%"
with that you will be able to count the number of occurence of "New_Folder*" and create one with the next number.
#echo off
set /a count=0
for /d %%d in (New_Folder*) do (
set /a count+=1
)
set /a count+=1
mkdir New_Folder%count%
Note that the best way would be to find the largest number at the end of New_Folder, but Windows Batch is very limitative and I'm a Linux guy!
EDIT : After about one hour of googling and testing :
#echo off
setlocal EnableDelayedExpansion
set max_number=0
for /d %%d in (New_Folder*) do (
set current_directory=%%~nxd
call:StrLength name_length !current_directory!
call:Substring directory_number,!current_directory!,10,!name_length!
if !directory_number! gtr !max_number! (
set max_number=!directory_number!
)
)
set /a max_number+=1
mkdir New_Folder%max_number%
:Substring
::Substring(retVal,string,startIndex,length)
:: extracts the substring from string starting at startIndex for the specified length
SET string=%2%
SET startIndex=%3%
SET length=%4%
if "%4" == "0" goto :noLength
CALL SET _substring=%%string:~%startIndex%,%length%%%
goto :substringResult
:noLength
CALL SET _substring=%%string:~%startIndex%%%
:substringResult
set "%~1=%_substring%"
GOTO :EOF
:StrLength
::StrLength(retVal,string)
::returns the length of the string specified in %2 and stores it in %1
set #=%2%
set length=0
:stringLengthLoop
if defined # (set #=%#:~1%&set /A length += 1&goto stringLengthLoop)
::echo the string is %length% characters long!
set "%~1=%length%"
GOTO :EOF
Note, the command line return me an error "The syntax of the command is incorrect." but everything works so I'll let you find why... New folder is created regardless of the order of directories or if they start at 1 or not :) Hope you'll enjoy!
This solution find the largest numbered name, and create the next one to it:
#echo off
for /d %%d in (New_Folder*) do set lastFolder=%%d
set /A nextFolder=%lastFolder:*New_Folder=% + 1
mkdir New_Folder%nextFolder%
EDIT: Previous solution doesn't correctly get the last numbered folder, but the next one is correct:
#echo off
setlocal EnableDelayedExpansion
set lastFolder=0
for /d %%d in (New_Folder*) do (
set folder=%%d
set folder=!folder:New_Folder=!
if not defined folder set folder=0
if !folder! gtr !lastFolder! set lastFolder=!folder!
)
set /A nextFolder=lastFolder+1
mkdir New_folder%nextFolder%
i am writing a batch script monotonic file renamer. basically, it makes the titles of all the files 1 2 3 4 .... and so on. i have since expanded it to be able to handle files of different types (txt, doc, flv, etc) but not everything is working out.
my main concern is i have broken the delayed expansion calls i was making before. now using !var1! is never expanded, or never recognized as a variable.
here is a verbosely commented version of my script
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET tempfile=temp.txt
SET exttemp=exttemp.txt
if [%1] == [] goto usage
::make sure your dont overwrite something useful
if EXIST %tempfile% (
ECHO Temp file already exists, are you sure you want to delete?
del /P %tempfile%
)
if EXIST %exttemp% (
ECHO EXT Temp file already exists, are you sure you want to delete?
del /P %exttemp%
)
::initialize
SET /a counter=0
SET type=
SET /a ender=%1
::write filenames to tempfile
DIR /B /ON > %tempfile%
::read lines one by one
for /f "usebackq delims=" %%a in (%tempfile%) do (
REM make sure we do not rename any of the working files
if NOT "%%a"=="renamer.bat" (
if NOT "%%a"=="temp.txt" (
if NOT "%%a"=="exttostr.bat" (
SET /a counter+=1
REM get file extension
exttostr %%a > %exttemp%
SET /P type= < %exttemp%
REM housekeeping
del /F %exttemp%
REM rename
ren %%a !counter!.!type!
ECHO Renamed "%%a" to "!counter!.!type!"
)))
REM exit when we have run enough
if "!counter!"=="!ender!" goto exit
)
goto exit
:usage
echo Usage: renamer NUMFILES
:exit
::final housekeeping
DEL temp.txt
the idea is i drop my two files, renamer.bat(this file) and exttostr.bat(helper to get the file extension) into the folder and run it, it will rename files sorted alphabetically from 1 to how ever many files i specify.
when i run the code, it never uses the variables marked for delayed expansion appropriately, always leaving them as "!varname!", so it renames the first file "!counter!.!type!" and throws errors for the rest because there is already a file in the directory with that name.
this brings me to a secondary issue. sorting the dir list alphabetically results in a poor handling of numbered files. for example the list:
"1 7 15 75 120"
is sorted:
"1 120 15 7 75"
i have not been able to find a way around this yet, only that it is indeed the intended result of the dir sort. the only workaround i have is padding numbers with enough zeroes in the front.
thanks in advance for any insight!
everything is sorted but the second problem. i think i have not spoken well. i have this issue when i take IN the directory file names, not when writing out. so they already need to be padded. i has hoping there was some other way to read the directory and have it be sorted appropriately.
the most promising thing i have found is here: http://www.dostips.com/DtCodeBatchFiles.php#Batch.SortTextWithNumbers
#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
)
this code can sort the filenames with one number in them (i.e. video100.mov is fine, video100video10.mov would break it)
the issue i have is i think adding a call to this helper fn will break it again, so i will be trying to include this in my modified renamer.bat now. any help is appreciated.
Probably the batch for extracting the extension reset the local environment.
But, you don't need it. You may extract the extension with the ~x option. Something similar to this ....
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
echo ren %%~fa !counter!%%~xa
)
)
goto :eof
to include leading zeroes in the counter, so that the directory sorts correctly, replace the previous rename command with three lines
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !counter!%%~xa
So putting all pieces together, add the monotonicrename function you just created in the batch file that can be as simpler as...
#echo off
setlocal enabledelayedexpansion
call :monotonicrename %1
goto :eof
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !zcounter!%%~xa
)
)
goto :eof
I didn't experience any issues with delayed expansion, everything worked fine for me (except, of course, for the fact that I didn't have the exttostr.bat helper script.)
Anyway, there are several things that could be improved about your script:
You don't need to store the result of DIR into a file to read it afterwards. You can read the output directly in the FOR loop.
You don't need the helper batch script. The extension can be extracted from %%a by using the ~x modifier with the loop variable: %%~xa. You can read more about modifiers by issuing HELP FOR from the command prompt.
The renamer batch file's own name can be referenced in the script as %0. You can apply the ~n modifier where you only need to use the name without the extension. The combined modifier of ~nx will give you the name with the extension.
So, here's how your script might look like with the above issues addressed:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=0
SET type=
SET /A ender=%1
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter!%%~xa"
ECHO Renamed "%%~a" to "!counter!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: %~n0 NUMFILES
As for your secondary issue, it can be easily resolved like this:
Use something like 100000 as counter's initial value. (Use however many 0s you like, but possibly no more than nine.) Add the same value to ender as well.
When renaming files, instead of !counter! use the expression that removes the first character (the 1): !counter:~1! (in fact, this is not about removal, but about extracting a substring starting from the offset of 1, learn more about it with the HELP SET command).
Here's the modified version of the above script:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=1000
SET type=
SET /A ender=%1
SET /A ender+=counter
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter:~1!%%~xa"
ECHO Renamed "%%~a" to "!counter:~1!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: renamer NUMFILES
You can also see that I made some other enhancements, like making sure the file name is enclosed in double quotes, and using GOTO :EOF instead of GOTO exit (:EOF is a special pre-defined label that points at the end of the batch script so you don't need to define your own).