Batch Files: Processing files in alphbetical (Numerical maybe>) Order - windows

I am currently trying to process a bunch of files with imagemagick
using a batch file in windows, they are all numbered numerically as
follows:
image00
image01,
image02,
...,
image010,
image011,
...,
image0100,
image0101
and so on, but when i try to process the file it wants to run though
image00, image01, image010, image0100, image0101, image0102 and so on.
my code is as follows
SETLOCAL EnableDelayedExpansion
SET COUNT=0
FOR %%a in (*.bmp) DO
(
IF !ERRORLEVEL!==0
(
SET TFILE=0!COUNT!
SET TFILE=Terrain!TFILE:~-4!.jpg
SET /A COUNT+=1
ECHO %%a >output.txt
convert %%a -compress LOSSLESS !TFILE!
)
)
is there any way i can make it so that it will process these files in order, for the time being i have a work-around but it means that i continually have to change some script files when the images are used later on. I would much rather have all of the files be the same 'Terrain' name with incrementing number following.
Thanks in advance guys!

you can probably create some more batch code to "sort" the numbers by order, however, since you can use imagemagick, i suppose you can also download other stuff. my suggestion is you can try and use GNU sort (in coreutils ). Then in your batch , do some thing like this
pseudocode :
for ... ( dir /b *bmp | gnu_sort -n ) do (
echo "do your stuff"
)
you could probably check whether the sort that comes with Windows has a numerical sorting option. (the last time i check it doesn't have this option).

You could rename the files to be image000 image001, image002, ..., image010,
You could split up the file name something like this:
#echo off
setlocal ENABLEDELAYEDEXPANSION
if not defined TRACE (
set TRACE=REM
)
%TRACE% On
for %%a In (data\*.*) do call :EachFile %%a
endlocal
goto :eof
:EachFile %%a
set Name=%~n1
#Echo %Name%
set NUm=%Name:~6,9%
set /a Num=Num+100000
#echo %Num%
echo ren %1 %~dp1Image%Num%%~x1
goto :eof

It's hard to find, because it's not really obvious from the documentation. The solution is to use the FOR command in combination with the dir command. I would do something like this:
#echo off
ECHO Listed in name order: %1
ECHO ------------------------------------------------------
FOR /F "tokens=*" %%G IN ('dir /b /o:n %1') DO echo %%G

Related

How to make a batch file loop resume from where it was by looping through the files listed on a .txt?

I want to start down mixing hundreds of surround media files in a folder to stereo, but this is a process that will take a lot of time. I'd like to create a batch file that executes my ffmpeg command to this set of files (probably listed in a .txt with dir /s /b) that I can run whenever my PC is on, but also keeps a record of already processed files to be excluded on the next run.
I know I can easily keep track of already processed files by simply adding something like if errorlevel 0 echo "%%~fg">>processed.txt to my loop, but I'm finding it challenging to come up with a way to ignore these files when running the script the next time.
Of course I could always manually edit the file list to be looped and remove the ones already processed, but I wonder if there is a clever way to do it programatically
An example of using a log with findstr. replace the definition of Set "SourceList=%~dp0list.txt" with the filepath of the file used to store the list of files for processing, or modify the for /f loop options to iterate over the output of your Dir command.
#Echo off
If not exist "%TEMP%\%~n0.log" break >"%TEMP%\%~n0.log"
Set "SourceList=%~dp0list.txt"
For /f "usebackq delims=" %%G in ("%sourceList%")Do (
%SystemRoot%\System32\Findstr.exe /xlc:"%%G" "%TEMP%\%~n0.log" >nul && (
Rem item already processed and appended to log. do nothing.
) || (
Rem item does not exist in log. Proccess and append to log.
Echo(Proccess %%G
>>"%TEMP%\%~n0.log" Echo(%%G
)
)
I managed to produce a way that doesn't rely on external tools and executes the job only with for loops. As it was needed to use UTF-8, extra steps were taken to properly revert the codepage after the batch file ended:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=*" %%G in ('%SystemRoot%\System32\chcp.com') do for %%H in (%%G) do set /A "CodePage=%%H" 2>nul
%SystemRoot%\System32\chcp.com 65001 >nul 2>&1
if not exist "%~dpn0.log" break >"%~dpn0.log"
set "FileList=%~dp0FileList.txt"
set "LogFile=%~dpn0.log"
for /f "usebackq delims=" %%G in ("%FileList%") do (
set "SkipFile="
for /f "usebackq delims=" %%H in ("%LogFile%") do (
if "%%G" == "%%H" (
set "SkipFile=1"
)
)
if not defined SkipFile (
echo --^> Processing file "%%~nG"
rem file is processed
) else (
echo "%%~nG" has already been processed
rem file is not processed
)
)
%SystemRoot%\System32\chcp.com %CodePage% >nul
endlocal
As can be seen, part of this answer is inspired by #T3RROR's!

.bat - Create a menu from folder file list

I don't usually create .bat file, but I made this little script useful for develop.
I'm using this for reading and creating a list of files contained into a folder:
for /f "delims=|" %%f in ('dir /b C:\src\release\android\') do echo %%f
and I found this about how to create a menu starting from a list of file -> Multiple choices menu on batch file?
Now my question is:
I'd like to create a menu with a list of files contained into that folder which I can select (not multiple selection) by pressing it's relative number on the list, but i don't really know how to merge the two bit of code above.
The final result should work something like:
[1] ..
[2] ..
[3] ..
[4] ..
select file:
and it will install the selected file from the folder.
Any suggestion would be really appreciated.
Thanks in advance
This should work unless you're using a version of Windows that doesn't have choice, like if you're still on XP for some reason.
#echo off
setlocal enabledelayedexpansion
set count=0
set "choice_options="
for /F "delims=" %%A in ('dir /a:-d /b C:\src\release\android\') do (
REM Increment %count% here so that it doesn't get incremented later
set /a count+=1
REM Add the file name to the options array
set "options[!count!]=%%A"
REM Add the new option to the list of existing options
set choice_options=!choice_options!!count!
)
for /L %%A in (1,1,!count!) do echo [%%A]. !options[%%A]!
choice /c:!choice_options! /n /m "Enter a file to load: "
:: CHOICE selections get set to the system variable %errorlevel%
:: The whole thing is wrapped in quotes to handle file names with spaces in them
:: I'm using type because I'm not familiar with adb, but you be able to get the idea
type "C:\src\release\android\!options[%errorlevel%]!"
Improving upon SomethingDark's script to run Python scripts in a user's Document folder (I know, not best practice here for brevity's sake), as it currently wouldn't work when there are more than 10 choices:
#echo off
setlocal enabledelayedexpansion
set count=0
set "choice_options="
for /F "delims=" %%A in ('dir /a:-d /b C:\Users\JohnSmith\Documents\*.py') do (
REM Increment %count% here so that it doesn't get incremented later
set /a count+=1
REM Add the file name to the options array
set "options[!count!]=%%A"
)
for /L %%A in (1,1,!count!) do echo [%%A]. !options[%%A]!
::prompts user input
set /p filechoice="Enter a file to load: "
:: Location of python.exe and location of python script explicitly stated
echo Running !options[%filechoice%]!...
"C:\Users\JohnSmith\AppData\Local\Microsoft\WindowsApps\python.exe" "C:\Users\JohnSmith\Documents\!options[%filechoice%]!"

Windows batch file to process camera images

I am trying to create a batch file to take photos off a camera or sd drive (which are specific and don't change.
I then want to move all the *.jpg to a different drive on the company's internal network.
The way it's set up it will look like n:\jobnumberfolder\pictures.jpg
I need the files to be able to be renamed like: "vpi(1).jpg", "vpi(2).jpg" and so on (the same thing you accomplish in Windows by highlighting multiple files and clicking rename.
The batch file will prompt the user for a job number (which will be the folder it will be moved to), and a description (what to name the file).
My programming experience is limited and in php and python, but I do understand the very basics: loops, if-else, arrays,... things like that.
My problem is that with the batch file, I cannot seem to find a good way to create a for loop to get what I want. I think I could do this in python using the os module, but I feel like I'm missing something simple (like a simple command or something I could be using for Windows).
I cannot even get a variable to increment, even with delayed expansion command. Even if I could, would it be possible to add it to the file name? I tried to find an answer for this, but have not been able to.
So my question are:
Can i actually do this in a batch file?
Would it be easier to just write a python script to do it, which will cause me to have to install python on the company's computer? I just want to be able to rename a file with a incrementing number at the end.
Something like this is what I'm looking for
i = 0
name = "whatever"
for each jpg in camera/images
rename each to whatever + i
i+=1
Any help would be appreciated.
Change n:\ in two places and f:\cam-folder\ where needed. It's untested:
#echo off
:loop
cls
echo Enter "jobnumberfolder,description" with comma and no quotes:
echo similar to 986,vpi
set "var="
set /p "var="
for /f "tokens=1,* delims=," %%a in ("%var%") do set "job=%%a"&set "desc=%%b"
echo "%job%" "%desc%" - correct? press enter to continue or N to start again:
set "var="
set /p "var="
if defined var goto :loop
md "n:\%job%\" 2>nul
setlocal enabledelayedexapansion
echo.
set c=0
for %%a in ("f:\cam-folder\*.jpg") do (
set /a c+=1
echo copying "n:\%job%\%desc%(!c!)%%~xa"
copy "%%a" "n:\%job%\%desc%(!c!)%%~xa" >nul
)
echo done
pause
#echo off
setlocal enableextensions enabledelayedexpansion
rem configuration
set "sourceFolder=w:\images"
set "targetFolder=n:\jobs"
rem test if there is something to copy
if not exist "%sourceFolder%\*.jpg" (
echo NO FILES FOUND
goto endProcess
)
:askJob
rem ask job number
set /p "jobNumber=Job Number : "
rem test if no job number
if "%jobNumber%"=="" (
echo NO JOB NUMBER - End of process
goto endProcess
)
rem test for existing job number
if exist "%targetFolder%\%jobNumber%" (
echo DUPLICATE JOB
goto askJob
)
rem ask for description
set /p "description=Description : "
rem test for no description
if "%description%"=="" (
echo NO DESCRIPTION - End of process
goto endProcess
)
rem WARNING - No test for valid job number nor valid description
rem WARNING - Don't shoot your own foot, or add code to validate
rem WARNING - according to your needs
rem create target job folder
set "target=%targetFolder%\%jobnumber%"
mkdir "%target%" > nul 2>nul
rem DO THE COPY
rem Generate a file list (dir), numerate it (findstr /n) and for each line
rem in generated list, split the number from the filename (for with delims)
rem and copy source file to numbered target file
for /f "tokens=1,* delims=:" %%f in ('dir /tc /od /b "%sourceFolder%\*.jpg" ^| findstr /n "^"') do (
echo transfer: "%sourceFolder%\%%g" == "%target%\%description%(%%f).*"
copy "%sourceFolder%\%%g" "%target%\%description%(%%f).*" >nul 2>nul
)
echo FILES COPIED
:endProcess
rem Clean
endlocal
rem and exit
exit /b

issues with enabledelayedexpansion for file renaming batch script

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).

batch find file extension

If I am iterating over each file using :
#echo off
FOR %%f IN (*\*.\**) DO (
echo %%f
)
how could I print the extension of each file? I tried assigning %%f to a temporary variable, and then using the code : echo "%t:~-3%" to print but with no success.
The FOR command has several built-in switches that allow you to modify file names. Try the following:
#echo off
for %%i in (*.*) do echo "%%~xi"
For further details, use help for to get a complete list of the modifiers - there are quite a few!
This works, although it's not blindingly fast:
#echo off
for %%f in (*.*) do call :procfile %%f
goto :eof
:procfile
set fname=%1
set ename=
:loop1
if "%fname%"=="" (
set ename=
goto :exit1
)
if not "%fname:~-1%"=="." (
set ename=%fname:~-1%%ename%
set fname=%fname:~0,-1%
goto :loop1
)
:exit1
echo.%ename%
goto :eof
Sam's answer is definitely the easiest for what you want. But I wanted to add:
Don't set a variable inside the ()'s of a for and expect to use it right away, unless you have previously issued
setlocal ENABLEDELAYEDEXPANSION
and you are using ! instead of % to wrap the variable name. For instance,
#echo off
setlocal ENABLEDELAYEDEXPANSION
FOR %%f IN (*.*) DO (
set t=%%f
echo !t:~-3!
)
Check out
set /?
for more info.
The other alternative is to call a subroutine to do the set, like Pax shows.

Resources