Renaming files in numerical series - batch-file

So I am using an application that creates a series of .tif files with very annoying filenames that do not sort correctly in alphanumerical order. I have no way to change the behavior of this commercial software, so I must use my own creativity to rename the files so they may be processed externally in the correct order. For example, the files created look like this:
image.tif, image(2).tif, image(3).tif, ..., image(9).tif, image(10).tif, ..., image(n).tif
Where n is an integer not less than 2. The first file has no number in parenthesis. Sorted alphanumerically, for example, the list would look like:
image.tif, image(10).tif, image(100).tif, image(1000), image(2).tif, ...
Obviously while this is the correct alphanumerical order, it's the incorrect numerical order. So with the aid of some batch commands, I created this:
#echo off & setlocal EnableDelayedExpansion
ren "image.tif" "a.tif"
ren "image(1).tif" "b.tif"
ren "image(2).tif" "c.tif"
ren "image(3).tif" "d.tif"
ren "image(4).tif" "e.tif"
ren "image(5).tif" "f.tif"
ren "image(6).tif" "g.tif"
ren "image(7).tif" "h.tif"
ren "image(8).tif" "i.tif"
ren "image(9).tif" "j.tif"
set a=1
for /f "delims=" %%i in ('dir /b *.tif') do (
ren "%%i" "!a!.tif"
set /a a+=1
)
This at least corrects the names of the first ten files, as I often process less than 100 files. The for loop is a snippet I found here on stackoverflow, it renames the alphanumeric files into integers starting with 1 up to the number of files.
Problem I run into is that this loop still creates 1.tif, 10.tif, and 100.tif that would come before 2.tif.
I've seen the use of %02d to format numbers into 01, 02, 10, etc. Does anyone know how to syntactically include that in the loop shown above?
Or if anyone can figure a more elegant method to rename these files into a suitable alphanumeric order, it would be greatly appreciated. I have the feeling my solution is very kludgey.

#echo off
setlocal EnableDelayedExpansion
rem Initialize "i" variable with the desired number of digits as zeros
set i=1000
rem Process the files in creation date-time order
for /F "delims=" %%a in ('dir /A-D /OD /B *.tif') do (
rem Increment the number: "1001"...
set /A i+=1
rem Omit first digit in number: "1", use the rest: "001"...
ren "%%a" "image(!i:~1!).tif"
)

#echo off
setlocal EnableDelayedExpansion
set /a max=99&set /a numlen=2
if exist "image(100).tif" set /a max=999&set /a numlen=3
for /L %%a in (1,1,%max%) do if exist "image(%%a).tif" set /a newnum=1000+%%a&if not exist "image(!newnum:~-%numlen%!).tif" echo(ren "image(%%a).tif" "image(!newnum:~-%numlen%!).tif"
set /a max+=1
echo(ren "image.tif" "image(!newnum:~-%numlen%!).tif"
[untested]
If "image(100).tif" exists, then we need to rename to (001)..(999). If not, to (01)..(99), so then number-length will be 3 or 2.
for each of the names 1..max, if the file exists, calculate 1000+the file number;take the last 3 or 2 characters and rename. Only attempt to rename if the destination name does not exist, so (6) will be renamed to (006) or (06), (15) to (05) or skipped as (15)=(15) and (123) will also be skipped as (123)=(123)
Finally, rename the image.tif file to (000) or (00)
Note that this will simply echo the required commands to allow verification. Change the echo(ren to ren when verified to actually perform the rename.

The %02d convention for padding numbers is a printf thing, not really applicable to the Windows cmd environment or batch scripting. PowerShell has something similar, but not pure batch. However, you can zero-pad numerals pretty easily using variable substring manipulation.
set /a a=0
for /f "delims=" %%I in ('dir /b *.tif') do (
set "idx=00!a!"
ren "%%~I" "!idx:~-3!%%~xI"
set /a a += 1
)
If you'd prefer to keep the naming convention of image(idx).tif while ensuring the TIF files remain in their intended sequential order, a lazy solution might be to use prename.bat and rename with regexps.
prename /v "s/\((\d)\)/(00\1)/" *.tif
prename /v "s/\((\d\d)\)/(0\1)/" *.tif

In cmd there are no format strings like %02d. However, you can use dir /B to retrieve a plain list of files, findstr to filter for those having a parenthesised numeric suffix in their names or no such suffix at all, for /F to capture the resulting file names and split them into tokens relying on (, ), and sub-string expansion to do the actual left-zero-padding, like in the following example:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_LOCATION=." & rem // (directory containing the files to rename)
set "_PATTERN=*.tif" & rem // (search pattern for the files to rename)
set /A "_DIGITS=4" & rem // (number of proposed digits from 1 to 10)
set "_REGEX1=^[^()][^()]*([0-9][0-9]*)\.tif$" & rem // (filter expression)
set "_REGEX2=^[^()][^()]*\.tif$" & rem // (filter expression)
rem // (the filter expressions ensure wrongly named files not to be processed)
rem // Build zero-padding string:
set "PAD=0000000000" & call set "PAD=%%PAD:~-%_DIGITS%%%"
rem // Loop through all matching files:
for /F "tokens=1-3 delims=()" %%A in ('
dir /B /O:N "%_PATTERN%" ^| findstr /I /R /C:"%_REGEX1%" /C:"%_REGEX2%"
') do (
rem // Check if current file name contains a parenthesised numeric suffix:
if not "%%B"=="" (
rem // Numeric suffix found, so store file name parts and pad number:
set "LEFT=%%A" & set "MID=%PAD%%%B" & set "RIGHT=%%C"
setlocal EnableDelayedExpansion
ECHO rename "!LEFT!(%%B)!RIGHT!" "!LEFT!(!MID:~-%_DIGITS%!)!RIGHT!"
endlocal
) else (
rem // No numeric suffix encountered, so append zero-suffix:
ECHO rename "%%A" "%%~nA(%PAD%)%%~xA"
)
)
endlocal
exit /B
After having successfully verified the appropriate output of the script, remove the upper-case ECHO command to actually rename any files.
The script renames a file like image.tif to image(0000).tif (supposing there is no file named image(0).tif, originally). If you do not want that to happen, simply remove the /C:"%_REGEX2%" part from the findstr command line.

Related

Batch Script To Identify Missing Numerical File Name

I have a custom service that automatically generates files every 60 mins into a particular directory with part of the filename incrementing numerically, Eg:
File_00004.job
File_00003.job
File_00002.job
File_00001.job
Currently I have an issue where on occasion a file isn't generated, which results in gaps in the file sequence. This issue then causes a number of issues if not identified ASAP.
I'd like a batch file to identify if I have a gap in the file name sequence.
Tried looking for solutions from existing posts, but haven't found something that fits, so apologies if this has been covered elsewhere.
#ECHO OFF
SETLOCAL enabledelayedexpansion
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
SET "mask=file_??????.job"
SET "lowest="
SET "highest="
FOR /f "delims=" %%a IN (
'dir /b /a-d /on "%sourcedir%\%mask%" '
) DO (
IF NOT DEFINED lowest SET "lowest=%%~na"
SET "highest=%%~na"
)
SET "lowest=%lowest:*_=1%"
SET "highest=%highest:*_=1%"
ECHO checking range %lowest:~1% to %highest:~1%
:: See whether an entry in the range is missing; report&create an empty file if so.
FOR /L %%a IN (%lowest%,1,%highest%) DO SET "name=%%a"&SET "name=file_!name:~1!.job"&IF NOT EXIST "%sourcedir%\!name!" echo !name! missing&(copy nul "%sourcedir%\!name!" >nul)
GOTO :EOF
Alternative structure for the for /L loop:
FOR /L %%a IN (%lowest%,1,%highest%) DO (
SET "name=%%a"
SET "name=file_!name:~1!.job"
IF NOT EXIST "%sourcedir%\!name!" (
echo !name! missing
copy nul "%sourcedir%\!name!" >nul
copy "d:\path to\template.file" "wherever\!name!" >nul
copy "d:\path to\template.file" "anotherplace\!name!" >nul
echo Batch is fun and powerful
copy "d:\path to\template.file" "a third place\!name!" >nul
)
)
The critical point is the positioning of the ( - must be directly after and on the same line as do or else or the logical comparison clause of if and must be matched by a ) (which doesn't need to be on its own line - I find it easier that way, to align indentation.) )s that are not intended to close a block need to be escaped with ^, thus: ^)

Batch file to copy file from specific sub folder inside a folder to destination folder

I have a folder structure like his
folder1-------|
|123456------123.txt
abc.txt
|234567------fgt.txt
234.txt
|abc---------ytr.txt
1234.txt
I need to copy files from Main folders sub directories if the sub folder has a length of 6(only numbers) . So .txt files from 123456,234567 will be copied and abc will not be copied.
I have tried using wildcards but not succeed yet. Thanks in advance.
>xcopy /s "C:\Users\xxx.xxx\Desktop\folder1\*\*.txt" "C:\Users\xxx.xxx\Documents\New folder" /l /D:09-09-2019
Ok, so here is an example:
#echo off
setlocal enabledelayedexpansion
for /f %%i in ('dir "C:\Users\xxx.xxx\Desktop\folder1\" /b /ad') do (
set "str=#%%i"
set "len=0"
for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!str:~%%a,1!" neq "" (
set /a "len+=%%a"
set "str=!str:~%%a!"
if !len! equ 6 if 1%%i equ +1%%i echo %%i is !len! characters
)
)
)
This will do a dir of the directories inside the path you give it. It will then test the length of the string, if the length is 6 characters, it will then test if the string is numeric, if true, it will echo the string (folder name) and the length of it. You should then test it as is and if it gives you the required output, you can replace echo portion with your copy string.
So your final solution will be something like:
#echo off
setlocal enabledelayedexpansion
set /p "mydate=Enter Date (format dd-mm-yyyy): "
for /f %%i in ('dir "C:\Users\xxx.xxx\Desktop\folder1\" /b /ad') do (
set "str=#%%i"
set "len=0"
for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!str:~%%a,1!" neq "" (
set /a "len+=%%a"
set "str=!str:~%%a!"
if !len! equ 6 if 1%%i equ +1%%i xcopy "C:\Users\xxx.xxx\Desktop\folder1\%%i\*.txt" "C:\Users\xxx.xxx\Documents\New folder" /D:!mydate!
)
)
)
You could use FindStr to isolate the directories matching your pattern:
batch-file:
#Echo Off
Set "SrcDir=%UserProfile%\Desktop\folder1"
Set "DstDir=%UserProfile%\Desktop\New folder"
For /F Delims^=^ EOL^= %%A In ('
"Dir /B/AD "%SrcDir%\*" 2>NUL|FindStr "^[0-9][0-9][0-9][0-9][0-9][0-9]$""
')Do XCopy "%SrcDir%\%%A\*.txt" "%DstDir%\" /D:09-09-2019 /Y>NUL 2>&1
Just modify the paths between the = and " on lines 2 and 3 to suit your actual source and destination directories.
Here is yet another method to achieve what you want (see all the explanatory rem remarks):
#echo off
rem // Use a `for /D` loop to iterate through all immediate sub-directories in the given root directory:
for /D %%I in ("%UserProfile%\Desktop\folder1\*") do (
rem // Store path and name of currently iterated directory in variables:
set "DIRECTORY=%%~I" & set "NAME=%%~nxI"
rem // Toggle delayed expansion to be able to write and to read a variable within the same block of code:
setlocal EnableDelayedExpansion
rem // Check if directory name holds more than 5 characters:
if not "!NAME:~5!" == "" (
rem // Check if directory name holds not more than 6 characters:
if "!NAME:~0,6!" == "!NAME!" (
rem // The directory name is exactly 6 characters long.
rem // Check whether the directory name consists of decimal figures by (mis-)using a `for /F` loop:
set "FLAG=" & for /F "tokens=1 delims=0123456789 eol=0" %%J in ("!NAME!") do set "FLAG=#"
rem // The flag variable has not been set, hence the directory name consists of decimal figures:
if not defined FLAG (
rem // Do the actual copy job at this point:
xcopy /I /Y /D:09-09-2019 "!DIRECTORY!\*.txt" "!UserProfile!\Documents\New Folder"
)
)
)
endlocal
)
You were trying to use wildcards not only in the last element of a path, which cannot be done (in Windows), but you can use a for /D loop to resolve the wildcards in upper directory levels, as demonstrated in the script.
The script uses ~-modifiers (which are the same for for meta-variables like %%I and for argument references like %1) to get the pure name of the currently iterated directory: %%~nxI.
To determine whether or not the directory name is exactly six (6) characters long, sub-string expansion is applied.
To determine whether or not the directory name consists of decimal digits only, a for /F loop is (mis-)used: for /F splits strings into parts at given delimiter characters (option delims); since all decimal digits are defined as delimiters, a directory named 1a2b3c would be split into the parts a, b and c (a becomes assigned to %%J then due to tokens=1, the other parts become dismissed), but a directory name 123456 leaves nothing behind since this is a delimiter-only string, in which case the for /F loop does not iterate at all. Together with a flag-like variable FLAG that becomes set in the body of the for /F loop we are able to determine whether or not the loop iterated, thus knowing whether the current directory name only consists of decimal digits.
The script writes to and reads from the variable NAME in the same block of code, which can normally not be done, unless you enable delayed variable expansion.
You can use the system environment variable %UserProfile% instead of specifying C:\Users\xxx.xxx, so the script may even work on a system where the user profile directory is not at the default location.

batch file - searching by character count

I would like to create a batch file that will search in a dir for all .pdf files that have a name of 10 characters as we have many .pdf's with different characters in name so I need to sort them out and move (cut and paste) them to a 2nd directory that is prepared. Can you please help me with this batch file?
example
setdir test contain .pdfs
--+6570296402-1-982464371-120.pdf
+6581239585-1-982470028-120.pdf
5710101306.pdf
0-PZ-6562825.pdf
0-PZ-545515247-1-982466351-120.pdf
5455152471.pdf
result:
target dir - test2 - where need to be moved .pdf with 10 characters
5710101306.pdf
5455152471.pdf
etc
Thank you so much
Running from the current directory you could probably do this using Where and Move:
#Echo Off
For /F "Delims=" %%A In ('Where/F .:??????????.pdf'
) Do Move /Y %%A "Test2">Nul
(for /f "delims=" %%a in ('dir /b /a-d *.pdf') do call :select10 "%%a") >filename.txt
... more processing if required
goto :eof
:select10
set "name=%~n1"
set "name=%name:~9%"
if not defined name goto :eof
set "name=%name:~1%"
if not defined name echo %~1
goto :eof
This should solve the problem.
perform a dir list of *.pdf, selecting filenames only. Pass the filename found to subroutine :select10, in quotes in case of spaces in filename.
The subroutine set name first to the name part of the filename received, then removes the first 9 characters. If the result is an empty variable, skip to end-of-file. If not, select all but the first character. If the result is not an empty string, the name must be 11 or more characters - if it's empty, then echo the name passed in the first instance.
The parentheses around the for command will cause the echoed data to be accumulated into the file nominated.
If you want to move the file to the destination, not simply list the selections, remove the ( before the for, and the ) >filename.txt after and replace the echo with move "%1" destination\
You could also do the same without using a subroutine as:
for /f %%a in ('dir /b /a-d *.pdf') do (
set "name=%%~na"
setlocal enabledelayedexpansion
set "name=!name:~9!"
if defined name (
set "name=!name:~1!"
if not defined name move "%%a" destination\
)
endlocal
)
Using delayed expansion to process the substringing operations.

Batch Script assistance needed

Happy Friday Think-Tank!
I need some assistance with a Batch .BAT script. Specifically I need help with some "IF statement syntax"
I have a script that is renaming files. There are two files, one ending in four digits and the other ending in five digits. The files will be renamed with variables I have already pre-set earlier within my script.
So here is a scenario: We have two files in a directory located at
c:\Users\username\Desktop\test-dir
There are two files within test-dir:
file1.12345
file2.1234
A four digit ending is one variable type (VAR1), whereas a file ending in five digits is another variable type (VAR2).
I need an if statement to:
a) read all the files(s) with the chosen directory (without using a wildcard if possible).
b) determine based on the number of digits after the "." which variable to use.
c) once making that determination rename the file with the appropriate variables.
The final re-naming convention is as so: yyyymmddtype.1234/12345
So basically it would use the datestamp variable I already created, the type variable I already created to be injected by the if statement, and append with the original ending digits of the file.
I know this seems like a lot, but I am more so a bash script guy. I have all the elements in place, I just need the if statement and what feels like a for loop of some kind to tie it all together.
Any help would be great!
Thank you!
Sorry, not the option you where asking for. Instead of iterating over the full list checking each file for extension conformance, iterate over a list of patterns that will filter file list, renaming matching files with the asociated "type"
for %%v will iterate over variable list, for %%a will split the content of the variable in pattern and type, for %%f will generate the file list, filter with findstr using the retrieved pattern and rename matching files with the corresponding "type"
Rename command is preceded with a echo to output commands to console. If the output is correct, remove the echo to rename the files.
#echo off
rem Variables defined elsewhere
set "folder=c:\somewhere"
set "timestamp=yyyymmdd"
rem Rename pattern variables in the form pattern;type
set "var1=\.....$;type1"
set "var2=\......$;type2"
set "var1=\.[^.][^.][^.][^.]$;type1"
set "var2=\.[^.][^.][^.][^.][^.]$;type2"
setlocal enableextensions disabledelayedexpansion
for %%v in ("%var1%" "%var2%") do for /f "tokens=1,* delims=;" %%a in ("%%~v") do (
for /f "tokens=*" %%f in ('dir /a-d /b "%folder%" ^| findstr /r /c:"%%~a"') do (
echo ren "%folder%\%%~f" "%timestamp%%%~b%%~xf"
)
)
endlocal
#ECHO OFF &SETLOCAL
set "yyyymmdd=yyyymmdd"
set "VAR1=VAR1"
set "VAR2=VAR2"
for /f "delims=" %%a in ('dir /b /a-d^|findstr /re ".*\....."') do echo(ren "%%~a" "%yyyymmdd%%VAR1%%%~xa"
for /f "delims=" %%a in ('dir /b /a-d^|findstr /re ".*\......"') do echo(ren "%%~a" "%yyyymmdd%%VAR2%%%~xa"
remove echo( to get it working.
If I understand you then this will rename the two files using preset variables for each one:
for %%a in ("%userprofile%\Desktop\test-dir\*") do (
if "%%~xa"==".12345" ren "%%a" "%variableA%-%variableB%%%~xa"
) else (
ren "%%a" "%variableC%-%variableD%%%~xa"
)
)

Batch rename using alternating alpha suffix

I am looking to batch rename a large number of files by adding a suffix of an alpha character, but only in a specific range. For example: File 126.pdf should be renamed 126A.pdf, File 127.pdf should be renamed 127B.pdf, File 128.pdf should be renamed 128A.pdf, File 129.pdf should be renamed 129B.pdf, etc. Any existing software that is capable of doing this is also appreciated.
Thanks!
setlocal EnableDelayedExpansion
rem Define the set of replacements
for %%a in ("126=126A" "127=127B" "128=128A" "129=129B") do set %%~a
rem Achieve the replacements
for %%a in (*.pdf) do (
if defined %%~Na (
ren "%%a" "!%%~Na!.pdf"
)
)
Previous program is based on the examples given in your request. Perhaps if you define the rename rules in a different, more precise way, the program may be entirely different!
You could try the following:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET suffixes=AB
FOR %%I IN (*.pdf) DO (
RENAME "%%I" "%%~nI!suffixes:~0,1!.*"
SET suffixes=!suffixes:~1!!suffixes:~0,1!
)
The suffixes variable is set to an "array" of all the possible suffixes to use with renaming.
When iterating over the files, the first suffix in the array is picked to form a new name. Then the suffixes are swapped in the array, so the other one becomes first at the next iteration.
This solution will work with an arbitrary number of suffixes.
You could use a FOR loop to examine each single filename.
setlocal EnableDelayedExpansion
for %%A in (*.pdf) DO (
echo Filename=%%A
set name=%%~nA
set "prefix="
if !name! == 123 set prefix=A
if !name! == 999 set prefix=B
echo rename "%%A" to "!name!!prefix!.pdf"
ren "%%A" "!name!!prefix!.pdf"
)

Resources