Removing multiple extensions while executing batch - batch-file

This is the first time I actually use batch.
I have multiple files I want to merge with ffmpeg:
3 - Making Your First Pickup Class.f303.webm
3 - Making Your First Pickup Class.f251.webm
I am trying to do it using the following script:
for %%g in (*.webm) do (
ffmpeg -y -i %%~ng.f251.webm -i %%~ng.f303.webm -c copy -map 0:v:0 -map 1:a:0 -shortest %%~ng.mp4
)
cmd /k
The problem is that I need to remove the last TWO extensions for this to work correctly.
E.g.: I am getting 3 - Making Your First Pickup Class.f303.f251.webm:
In short, I need what will look like %%~n(%~ng)

Here is one solution:
#echo off
setlocal EnableDelayedExpansion
for %%G in (*.f303.webm) do (
set "FileName=%%~nG"
set "FileName=!FileName:~0,-5!"
ffmpeg.exe -y -i "!FileName!.f251.webm" -i "!FileName!.f303.webm" -c copy -map 0:v:0 -map 1:a:0 -shortest "!FileName!.mp4"
)
endlocal
The FOR loop does not search anymore for any file with extension .webm, but instead just for one of the two files to merge. This avoids the double merge.
The string .f303 is removed by assigning file name without .webm to an environment variable which is copied to same variable without the last 5 characters.
It is necessary to use delayed expansion because the environment variable FileName is modified within a block defined with (...).
Another method would be using a subroutine as demonstrated below:
#echo off
for %%G in (*.f303.webm) do call :Merge "%%~nG"
goto :EOF
:Merge
ffmpeg.exe -y -i "%~n1.f251.webm" -i "%~n1.f303.webm" -c copy -map 0:v:0 -map 1:a:0 -shortest "%~n1.mp4"
exit /B
Double quotes are necessary around all file names because of the spaces in the *.webm file names.
%~n1 is replaced by file name of first argument without file extension which means the substring of first argument from first character after last backslash (if present at all) to last character before last dot in string being interpreted as separator between file name and file extension.
For the command processor it does not matter in most cases if first argument is really the name of an existing file or an existing folder on determining "file/folder" name (%~n1), "file" extension (%~x1) and "file/folder" path with drive (%~dp1).
The file or folder must be found only if the string of first argument is incomplete to determine the string. For example if just name of a file with file extension but without drive and path is passed as first argument and %~dp1 is used somewhere, the command processor must find the file or folder to determine drive and path for this file/folder.
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 briefly usage of a subroutine as well as %~n1.
echo /?
endlocal /?
exit /?
for /?
goto /?
set /?
setlocal /?

Related

Batch script calling its own name instead of files of a certain type?

cd %cd%
ffmpeg -i %cd%/%04d.png out.mp4
A script with just this in it works completely fine and outputs exactly what I need it to, but:
:: A simple script to convert a png or jpg image sequence to an
mp4 file with ffmpeg
cls
#echo off
title PNG2MP4
color C
echo Ensure you have ffmpeg installed and setup in your environment variables or this script won't work.
:QUERY
echo This will convert all image files in the following directory to a single mp4,
echo %cd%
echo are the files PNGs or JPEGs(PNG/P/JPG/J/CANCEL)?
set/p "ch=>"
if /I %ch%==PNG goto CONVERTPNG
if /I %ch%==P goto CONVERTPNG
if /I %ch%==JPG goto CONVERTJPG
if /I %ch%==J goto CONVERTJPG
if /I %ch%==CANCEL goto :eof
echo Invalid choice & goto QUERY
:CONVERTPNG
cd %cd%
ffmpeg -i %cd%/%04d.png out.mp4
:CONVERTJPG
cd %cd%
ffmpeg -i %cd%/%04d.jpg out.mp4
This more complex version of the script fails, outputting:
C:\tmp/img2mp4.bat4d.jpg: No such file or directory
Why is it no longer calling the files that it did before and is there an easy fix for this?
Here is my suggestion for the batch file:
#echo off
rem A simple script to convert a png or jpg image sequence to an mp4 file with ffmpeg
cls
title PNG2MP4
color C
echo Ensure you have ffmpeg installed and setup in your environment variables
echo or this script won't work.
echo/
echo This will convert all image files in the following directory to a single mp4:
echo/
echo %cd%
echo/
%SystemRoot%\System32\choice.exe /C PJC /N /M "Are the files PNGs or JPEGs or Cancel (P/J/C)? "
if errorlevel 3 color & goto :EOF
echo/
if errorlevel 2 (
ffmpeg.exe -i %%04d.jpg out.mp4
) else (
ffmpeg.exe -i %%04d.png out.mp4
)
color
The character % must be escaped in a batch file with one more % to be interpreted as literal character which was the main problem causing the batch file not working as expected. %0 references the string used to start the batch file which was img2mp4.bat. So %04d.jpg concatenated img2mp4.bat with 4d.jpg and the result was running ffmpeg.exe with img2mp4.bat4d.jpg as file name instead of the argument string %04d.jpg.
To reference one or more files/folders in current directory the file/folders can be simply specified in arguments list of a script or application with no path. This is explained in Microsoft documentation about Naming Files, Paths, and Namespaces. This page describes further that on Windows the directory separator is the backslash character \ and not the forward slash / as on Linux and Mac. / is used on Windows mainly for options as it can be seen on line with command CHOICE because of this character is not possible in file/folder names. - is used on Linux/Mac for options which is possible also in file/folder names even as first character of a file/folder name. So on Windows \ should be always used as directory separator although the Windows kernel functions for file system accesses automatically correct / in file/folder names to \.
CHOICE is much better for prompting a user to take a choice from several offered options than the command SET with option /P. set/p is syntactically not correct at all because of command set should be separated with a space from option /P which should be separated with a space from next argument variable=prompt text. set/p forces cmd.exe to automatically correct the command line to set /p. Batch files should be syntactically correct coded and not depend on automatic error corrections of Windows command processor.
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 how to reference batch file arguments.
echo /?
rem /?
cls /?
title /?
color /?
set /?
choice /?
if /?
goto /?
Further I suggest to read:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
Single line with multiple commands using Windows batch file
ECHO. FAILS to give text or blank line - Instead use ECHO/
Where does GOTO :EOF return to?

How to run ffmpeg in a scheduled batch file with a file exclusion list?

I have the following batch file created to run as a schedule task. It auto encodes the audio to AAC in my main media folder. Everything is working, but now I need to do 2 more things:
Delete the original upon completion.
Set it so that the next time it runs, it does not try to convert the already converted files.
Is that possible? How would that look? Is there a way to "disregard" file names with CONVERTED in them?
for /r "C:\Users\USER\Desktop\TEST" %%a in ("*.mkv") do ffmpeg -i "%%a" -vcodec copy -acodec aac -ac 2 -ab 256K %%~dpnaCONVERTED.mkv
This could be done with following batch file:
#echo off
for /F "eol=| delims=" %%I in ('dir "%UserProfile%\Desktop\TEST\*.mkv" /A-D-H /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /E /I /L /V /C:CONVERTED.mkv 2^>nul') do (
ffmpeg.exe -i "%%I" -vcodec copy -acodec aac -ac 2 -ab 256K "%%~dpnICONVERTED.mkv"
if not errorlevel 1 del "%%I"
)
The command FOR executes in a separate command process started with cmd.exe /C in background (= not visible) the command line:
dir "C:\Users\USER\Desktop\TEST\*.mkv" /A-D-H /B /S 2>nul | C:\Windows\System32\findstr.exe /E /I /L /V /C:CONVERTED.mkv 2>nul
So the command process in background with no console window is first running command DIR which outputs
all non hidden files because of /A-D-H (attribute not directory and not hidden)
matching the wildcard pattern *.mkv
in bare format because of /B which means file name only
in specified directory and all its subdirectories because of /S
with full path also because of /S
to handle STDOUT.
DIR outputs an error message if it can't find any *.mkv file. This error message written to handle STDERR is suppressed by redirecting it to device NUL with 2>nul.
The file names with full path output by DIR line by line to handle STDOUT are redirected with | to handle STDIN of next command FINDSTR.
FINDSTR searches in the list of file names for
comparison string CONVERTED.mkv
at end of a line because of /E
case-insensitive because of /I
and literally because of /L
and outputs the inverted result because of /V.
That means FINDSTR outputs the lines not ending with case-insensitive, literally interpreted comparison string CONVERTED.mkv to ignore the *.mkv file converted already in a previous execution of the batch file.
It is possible that FINDSTR can't find any line matching the search criteria and therefore outputs an error message to handle STDERR which is suppressed by redirecting it with 2>nul to device NUL.
The command FOR captures the output written to handle STDOUT of the background command process which is the output of DIR filtered with FINDSTR and then processes this output line by line.
Empty lines are ignored by FOR. But that does not matter here as the list of file names with full path does not contain empty lines.
Lines starting with a semicolon would be also ignored by FOR, but this behavior is modified with eol=| to ignore just lines starting with a vertical bar. A vertical bar is impossible here as no directory or file can contain a vertical bar in name. Well, it is also practically impossible that a file name with full path starts with a semicolon. So eol=| would not be really needed here.
FOR with parameter /F splits by default a line up into substrings using space and horizontal tab as string delimiters and assign only the first space/tab delimited string to specified loop variable I. This split behavior is not wanted here as needed in body command block of FOR is always the full qualified name of an MKV file even on path or file name contains a space character. For that reason delims= is specified to define an empty list of delimiters which disables splitting up the lines into substrings.
ffmpeg.exe is executed which I don't have installed and so I can only assume that it exits with 0 on success and with any value greater 0 on an error.
The exit code of an executed application or command is assigned by Windows command interpreter to environment variable ERRORLEVEL.
The IF condition checks if ffmpeg.exe exited not with a value greater or equal 1, i.e. with 0 (or a negative value) for success. In this case the input file is deleted as wanted to prevent a conversion of same file once again on next run of this batch file.
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.
dir /?
echo /?
findstr /?
for /?
if /?
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.

Drag and Drop multiple files onto batch

Please keep in mind that I'm a newb.
I need to drag and drop 244 files with a .tex extension into a batch that then creates a .png that I can edit. Simply selecting them all and dropping them in isn't doing the trick, so someone wrote me a code that I have no idea how to properly use:
for %%f in (*.tex) do c:\python27\python.exe tools/textool.py -x -v -ra %%f
The .tex files are all in the same directory of the batch, which is in C:\users\myname\downloads\folder1\folder2\folder3. Hope you can help.
Try this batch code:
#echo off
if "%~1" == "" goto :EOF
for %%I in ("%~1\*.tex") do C:\python27\python.exe "%~dp0tools\textool.py" -x -v -ra "%%~fI"
The batch file does nothing if called without a parameter.
But in case of batch file is called with a parameter, it expects without verification (could be added) that the (first) parameter specifies a folder path in which all *.tex files should be processed by the Python script.
An alternate solution:
#echo off
if "%~1" == "" goto :EOF
pushd "%~1"
if errorlevel 1 goto :EOF
for %%I in ("*.tex") do C:\python27\python.exe "%~dp0tools\textool.py" -x -v -ra "%%~nxI"
popd
The directory specified as parameter becomes the current working directory for this batch file and the Python script gets just file name with file extension passed via command line parameter.
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:
%~1 ... first parameter without surrounding double quotes
%~dp0 ... drive and path of argument 0 which is path of folder containing the batch file ending with a backslash.
echo /?
for /?
goto /?
if /?
popd /?
pushd /?
Note: It would be much faster to modify the Python script to search itself for all *.tex files in folder path specified on command line as parameter for conversion of each found file matching the pattern to a *.png file.

Why is a subfolder with space recognized as file on execution of my batch script?

In my code I'm searching for only files in folder and all subfolders. When the name of subfolder has one blank (space) between the words this subfolder is recognized as a file, too. This is not correct behavior. The parameter /a-d doesn't help here.
#echo on
Setlocal EnableDelayedExpansion
set "input=C:\Users\NekhayenkoO\test\"**
set "output=C:\Users\NekhayenkoO\outputxml\"**
set string1=Well-Formed and valid
set string2=Well-Formed, but not valid
set string3=Not well-formed
set /a loop=0
set /a loop1=0
set /a loop2=0
set /a loop3=0
for /f %%a in ('dir /b /a-d /s %input%') do (
CALL jhove -m PDF-hul -h xml -o %output%\%%~na.xml %%a
if !ERRORLEVEL! EQU 0 (echo Errorlevel equals !errorlevel! )
if !ERRORLEVEL! GEQ 1 (Errorlevel equals !errorlevel! )
set /a loop3+=1
)
The output of the script on running in directory C:\Users\NekhayenkoO\jhove-beta:
Setlocal EnableDelayedExpansion
set "input=C:\Users\NekhayenkoO\test\"**
set "output=C:\Users\NekhayenkoO\outputxml\"**
set string1=Well-Formed and valid
set string2=Well-Formed, but not valid
set string3=Not well-formed
set /a loop=0
set /a loop1=0
set /a loop2=0
set /a loop3=0
for /F %a in ('dir /b /a-d /s "C:\Users\NekhayenkoO\test\"') do (
echo Verarbeite %~na
CALL jhove -m PDF-hul -h xml -o "C:\Users\NekhayenkoO\outputxml\\%~na.xml" "%a"
if !ERRORLEVEL! EQU 0 (echo Errorlevel equals !errorlevel! )
if !ERRORLEVEL! GEQ 1 (Errorlevel equals !errorlevel! )
set /a loop3+=1
)
(
echo Verarbeite 757419577
CALL jhove -m PDF-hul -h xml -o "C:\Users\NekhayenkoO\outputxml\\757419577.xml" "C:\Users\NekhayenkoO\test\757419577.pdf"
if !ERRORLEVEL! EQU 0 (echo Errorlevel equals !errorlevel! )
if !ERRORLEVEL! GEQ 1 (Errorlevel equals !errorlevel! )
set /a loop3+=1
)
Verarbeite 757419577
Errorlevel equals 0
Verarbeite GBV58575165X
Errorlevel equals 0
Verarbeite GBV85882115X
java.lang.ClassCastException: edu.harvard.hul.ois.jhove.module.pdf.PdfSimpleObject cannot be cast to edu.harvard.hul.ois.jhove.module.pdf.PdfDictiona
at edu.harvard.hul.ois.jhove.module.PdfModule.readDocCatalogDict(PdfModule.java:1344)
at edu.harvard.hul.ois.jhove.module.PdfModule.parse(PdfModule.java:521)
at edu.harvard.hul.ois.jhove.JhoveBase.processFile(JhoveBase.java:803)
at edu.harvard.hul.ois.jhove.JhoveBase.process(JhoveBase.java:588)
at edu.harvard.hul.ois.jhove.JhoveBase.dispatch(JhoveBase.java:455)
at Jhove.main(Jhove.java:292)
Errorlevel equals 0
Verarbeite GBV858852357
Errorlevel equals 0
Verarbeite nicht_valide_PDF
Errorlevel equals 0
Verarbeite not_Wellformed_intern
Errorlevel equals 0
Verarbeite pp1788_text
Errorlevel equals 0
Verarbeite Rosetta_Testdatei
Errorlevel equals 0
Verarbeite script
Errorlevel equals 0
Verarbeite java
Errorlevel equals 0
Verarbeite java
Errorlevel equals 0
Verarbeite java
Errorlevel equals 0
Verarbeite java
Errorlevel equals 0
Verarbeite GBV58525785X
Errorlevel equals 0
Verarbeite GBV58574517X
Errorlevel equals 0
Drücken Sie eine beliebige Taste . . .
What is jhove?
Oleg Nekhayenko, you have asked several jhove related questions in the last days, but you have always forgotten to explain what jhove is which is important to know for all of your questions.
Therefore I searched in world wide web for jhove, found very quickly the homepage
JHOVE | JSTOR/Harvard Object Validation Environment, read quickly its documentation and command-line interface description and finally downloaded also jhove-1_11.zip from SourceForge project page of JHOVE.
All this was done by me to find out that jhove is a Java application which is executed on Linux and perhaps also on Mac using the shell script jhove and on Windows the batch file jhove.bat for making it easier to use by users.
You could have saved yourself and all readers of your questions a lot of time if you would have written jhove.bat instead of just jhove in your code snippets or at least mentioned anywhere that jhove is a batch file.
Assigning a value/string to an environment variable
I suggest to read first the answer on
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
and next look on these two lines:
set "input=C:\Users\NekhayenkoO\test\"**
set "output=C:\Users\NekhayenkoO\outputxml\"**
I don't know why two asterisks are at end of those 2 command lines. But that does not really matter as both asterisk are ignored on assigning the two paths to the two environment variables.
This can be seen on posted output of the batch file as there is no asterisk output on the lines:
for /F %a in ('dir /b /a-d /s "C:\Users\NekhayenkoO\test\"') do (
CALL jhove -m PDF-hul -h xml -o "C:\Users\NekhayenkoO\outputxml\\757419577.xml" "C:\Users\NekhayenkoO\test\757419577.pdf"
There is no asterisk anywhere. So the environment variables input and output are obviously defined without the asterisks at end which is even good here.
Enclosing directory and file names in double quotes
The help output on running cmd /? in a command prompt window explains in last paragraph on last help page on which characters in a directory or file name double quotes must be used around complete directory/file name.
The space character is the string delimiting character on command line and therefore a directory or file name with a space must be always enclosed in double quotes.
Predefined environment variables on Windows
Opening a command prompt window and running set results in output of all environment variables defined for the the current user account including PATH and PATHEXT as also USERNAME and USERPROFILE.
The Wikipedia article about Windows Environment Variables explains the environment variables predefined by Windows. It is advisable to make use of them in batch files.
Execution of applications and scripts on Windows
If in a command prompt window or in a batch file just the file name of an application or script without file extension and without path is specified, the Windows command interpreter is searching first in current directory and next in all directories of environment variable PATH for a file with specified name having a file extension listed in environment variable PATHEXT. In this case Windows command interpreter is searching for jhove.*.
The values of the environment variables PATH and PATHEXT can be seen on opening a command prompt window and running in this window set path which results in output of all environment variables starting with the case-insensitive interpreted string PATH with their current values.
Next to know is that when Windows command interpreter searches for jhove.*, the NTFS file system returns the file names matching this search pattern sorted alphabetically. So if current directory or any of the directories listed in PATH have for example jhove.bat and jhove.exe, the NTFS file system returns first jhove.bat. This batch file is used by Windows command interpreter as file extension BAT is listed by default in PATHEXT.
But if the file system of the drive with jhove.* files is FAT, FAT32 or ExFat, the file system returns the file names matching the search pattern in order as stored in the file allocation table and therefore unsorted. So in case of a directory contains jhove.bat and jhove.exe on a drive with any FAT file system, it is unpredictable which file is executed by Windows command interpreter on specifying just jhove in a batch file.
For that reason it is always advisable to specify the application or script with file name and at least also with the file extension. And if possible the entire path to the application to run or the script to call should be also specified.
The Windows command interpreter does not need to search around by specifying the name of an application or script file with file extension and with complete path.
See also answer on Where is "START" searching for executables?
Calling a batch file versus running an application
A batch file is a script (text file) interpreted by Windows command interpreter line by line whereby a command block starting with ( and ending with matching ) is interpreted like a subroutine defined on one line.
An application is an executable (binary file) compiled with a compiler for a specific processor or processor family and therefore does not need to be interpreted anymore on execution. It contains already processor instructions (machine code).
Why the command call must be used to run another batch file from within a batch file is explained in detail by the answers on
How to call a batch file that is one level up from the current directory?
In a Windows batch file, can you chain-execute something that is not another batch file?
For that reason it is very important to know what jhove is. It is a batch file and must be therefore called with command call which answers the question How to process 2 for loops after each other in batch?
For help on command call open a command prompt window and run call /?. The output help explains also which placeholders exist to reference arguments of the batch file whereby argument 0 is the name of the batch file.
Which command lines contains jhove.bat?
On unexpected behavior on calling a batch file from another batch file it is important to know the code of the called batch file as well because the error could be in code of called batch file.
Code of jhove.bat as stored in jhove-1_11.zip without instruction comments:
#ECHO OFF
SET JHOVE_HOME=%~dp0
SET EXTRA_JARS=
REM NOTE: Nothing below this line should be edited
REM #########################################################################
SET CP=%JHOVE_HOME%\bin\JhoveApp.jar
IF "%EXTRA_JARS%"=="" GOTO FI
SET CP=%CP%:%EXTRA_JARS
:FI
REM Retrieve a copy of all command line arguments to pass to the application
SET ARGS=
:WHILE
IF %1x==x GOTO LOOP
SET ARGS=%ARGS% %1
SHIFT
GOTO WHILE
:LOOP
REM Set the CLASSPATH and invoke the Java loader
java -classpath %CP% Jhove %ARGS%
Well, this is a not good written batch code for following reasons:
The commands setlocal and endlocal are not used in batch file to control the life time of variables used by this batch file. See answer on change directory command cd ..not working in batch file after npm install for more details. npm.bat is also a not good coded batch file like jhove.bat as it turned out.
The command line SET JHOVE_HOME=%~dp0 defines the environment variable JHOVE_HOME with drive and path of storage location of jhove.bat. The path returned by %~dp0 ends always with a backslash. If jhove*.zip was extracted into a directory with 1 or more space in complete path, care must be taken wherever JHOVE_HOME is finally used to enclose the final string in double quotes.
The command line SET CP=%JHOVE_HOME%\bin\JhoveApp.jar defines the environment variable CP by concatenating path to batch file jhove.bat with a fixed path and name of the Java package. Here is already a small mistake as %~dp0 is a path always ending with a backlash concatenated with a string starting with a backslash. So there are two backslashes finally in path to the Java package file. But Windows kernel handles this error in path and therefore it does not really matter.
The environment variable CP is referenced unmodified with no EXTRA_JARS defined by the user finally on command line java -classpath %CP% Jhove %ARGS%. The error here is %CP% is specified without being enclosed in double quotes which results in unexpected behavior if jhove*.zip was extracted indeed by the user into a directory with 1 or more spaces in complete path.
A percent sign is missing at end of command line SET CP=%CP%:%EXTRA_JARS.
The writer of jhove.bat did not know obviously anything about %* which on usage of last command line instead of %ARGS% makes the WHILE loop above completely useless.
Much better for jhove.bat would be:
#echo off
setlocal EnableExtensions
set "JHOVE_HOME=%~dp0"
set "EXTRA_JARS="
REM NOTE: Nothing below this line should be edited
REM #########################################################################
set "CP=%JHOVE_HOME%bin\JhoveApp.jar"
if not "%EXTRA_JARS%"=="" set "CP=%CP%:%EXTRA_JARS%"
rem Set the CLASSPATH and invoke the Java loader
java.exe -classpath "%CP%" Jhove %*
endlocal
The executable java.exe must be found via environment variable PATH by Windows command interpreter.
Final batch code for usage
I suggest to use the following code for this task in case of jhove.bat should not be modified to above working code:
#echo off
setlocal EnableExtensions
set "InputFolder=%USERPROFILE%\test"
set "OutputFolder=%USERPROFILE%\outputxml"
echo Searching for bin\JhoveApp.jar in:
echo.
set "SearchPath=%CD%;%PATH%"
set "SearchPath=%SearchPath:)=^)%"
for /F "delims=" %%I in ('echo %SearchPath:;=^&ECHO %') do (
echo %%I
if exist "%%~I\bin\JhoveApp.jar" (
set "JHOVE_HOME=%%~I"
goto RunJHOVE
)
)
echo.
echo Error reported by %~f0:
echo.
echo Could not find bin\JhoveApp.jar in current directory and folders of PATH.
echo.
endlocal
pause
goto :EOF
:RunJHOVE
if "%JHOVE_HOME:~-1%" == "\" (
set "CP=%JHOVE_HOME%bin\JhoveApp.jar"
) else (
set "CP=%JHOVE_HOME%\bin\JhoveApp.jar"
)
echo.
echo Using %CP%
md "%OutputFolder%" 2>nul
rem for /F %%I in ('dir /A-D /B /S "%InputFolder%\*" 2^>nul') do (
rem java.exe -classpath "%CP%" Jhove -m PDF-hul -h xml -o "%OutputFolder%\%%~nI.xml" "%%I"
rem )
for /R "%InputFolder%" %%I in (*) do (
java.exe -classpath "%CP%" Jhove -m PDF-hul -h xml -o "%OutputFolder%\%%~nI.xml" "%%I"
)
endlocal
The input and output folder paths are defined without backslash at end and without asterisk using predefined environment variable USERPROFILE.
A slightly modified code written by Magoo in his answer on Find the path used by the command line when calling an executable is used to find Java package of JHOVE. The batch file prints the folders it is searching for in case of the file could not be found which results in an error message and halting batch execution until the user presses any key.
The class path variable CP is created with taking into account if folder path ends with a backslash or not. Folder paths in PATH should be defined without backslash at end, but there are always installers which add folder paths not 100% correct to PATH. However, it does not really matter if the result would be \\ anywhere within a path as Windows kernel handles this. That's the reason why if exist "%%~I\bin\JhoveApp.jar" also always works although this file existence test could be also done with two backslashes in path depending on folder path in PATH.
Next the output folder is created without checking first if the folder is already existing and without checking if folder creation was successful at all.
The batch code contains two solutions for running jhove on each file found recursively in input folder path. The first one is commented out. It would have the advantage to work also for hidden and system files. The second solution does not work for hidden and system files, but this is most likely not necessary here. The second solution is therefore the preferred one.
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.
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
pause /?
set /?
setlocal /?
And read also the Microsoft articles:
Microsoft's command-line reference
Using command redirection operators
Testing for a Specific Error Level in Batch Files

Loop through subfolders of subfolders and execute a command on each file of the last subfolder

Consider this hierarchy:
MainFolder\Sub_Folder1\Original_Files\
\Converted_Files\
\Sub_Folder2\Original_Files\
\Converted_Files\
Now in each ...\Original_Files\ I've a bunch of video files which I'll encode and save to the respective ...\Converted_Files\.
I could do it for one subfolder with this batch code:
#echo off
set "sourcedir=G:\Animation\Anime\OnePiece\Episodes\Main"
set "outputdir=G:\Animation\Anime\OnePiece\Episodes\Converted"
PUSHD "%sourcedir%"
for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"
POPD
I've generated a text file with the folder paths of all the subfolders which contains:
G:\Animation\ToConvert\Berserk_1997_The_Complete_Series
G:\Animation\ToConvert\Blue_Exorcist
G:\Animation\ToConvert\Elfen_Lied
Every folder listed in the file has Main and Converted folders within them. I've to loop through all files in Main and save into Converted as you might see in above.
This is something I came up with :
#echo off
for /F "tokens=*" %%A in (f.txt) DO (
set "sourcedir=%A%\Main"
set "outputdir=%A%\Converted"
PUSHD "%sourcedir%"
for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"
POPD
) %%A
Running for /F "tokens=*" %A in (f.txt) DO #echo %A gives me the names of the subfolders.
I thought somehow if I could pass the name to some variable and concatenate \Main and \Converted to it, it might work.
But on running the code above from within a command prompt window, it's just switching the current directory from the folder I'm running the batch file to C:\Windows.
How can I run nested loops, one for the subfolders and then chose between working in Main and saving in Converted and the next loop for files in Main?
Your last batch code fails because of
referencing the loop variable A like an environment variable with %A% instead of %%A and
referencing environment variables defined/set within a command block defined with ( and ) requires the usage of delayed expansion enabled before with the command line setlocal EnableDelayedExpansion and using !sourcedir! and !outputdir! instead of %sourcedir% and %outputdir% which are already replaced by current value of the environment variables sourcedir and outputdir (empty string here as not defined before) when Windows command processor parses the entire command block before executing command FOR the first time.
%%A after closing parenthesis at end is unknown for Windows command interpreter and results therefore in an exit of batch processing because of a syntax error.
However, better than your code which requires first the creation of a text file with the folder paths would be the usage of following code:
#echo off
for /D %%D in ("G:\Animation\ToConvert\*") do (
if exist "%%D\Main\*.mkv" (
echo Processing %%D ...
if not exist "%%D\Converted\*" md "%%D\Converted"
for %%I in ("%%D\Main\*.mkv") do (
ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%D\Converted\%%~nxI"
)
)
)
The outer FOR with parameter /D finds just non hidden subfolders within folder G:\Animation\ToConvert and holds in loop variable D the name of the subfolder with full path not ending with a backslash.
The IF condition checks if in the current subfolder there is a folder Main with 1 or more *.mkv files to process. If this condition is true,
an information message is output to see progress on running the batch file,
in current subfolder the folder Converted is created if not already existing,
another FOR loop is executed to process each *.mkv file found in the folder Main of current subfolder.
The loop variable I holds the name of the current *.mkv file with full path. So "%%I" can be used for the input file as current directory does not matter because input file name is with full path.
For the output file the folder Converted in current subfolder is specified and appended is with %%~nxI the file name and the file extension of input file as name for the output file.
This batch code does not require delayed expansion as there is no environment variable used, only the loop variables D and I.
For completeness also your code using a text file containing line by line the folders to process with removing all unnecessary environment variables to make it possible to run the batch file without using the commands setlocal and endlocal.
#echo off
for /F "usebackq tokens=*" %%A in ("f.txt") do (
if exist "%%A\Main\*.mkv" (
echo Processing %%A ...
if not exist "%%A\Converted\*" md "%%A\Converted"
for %%I in ("%%A\Main\*.mkv") do (
ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxI"
)
)
)
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.
echo /?
for /?
if /?
md /?
BTW: See this answer and the other answers linked there if you ever want to understand what delayed expansion is and what the commands setlocal and endlocal do not needed here.
#Mofi wrote a great answer, both his codes work flawlessly. This is just a simpler version I am running because the conditions being checked in that program are already met.
#echo off
for /F "tokens=*" %%A in (f.txt) DO (
for %%F in (%%A\Main\*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxF"
)

Resources