Batch File: Escaping ! within For Loop - batch-file

I am trying to create a for loop that will go through a directory and run an ffmpeg command to all files within that directory. My problem is that some of the files have ! in their name which causes problems when using delayed expansion. I have researched many solutions and none of them are getting me anywhere. Currently I can echo the file names with the ! included, but I cannot seem to pass the file name with the ! into my for loop as the delayed expansion effectively removes the !.
Here is my batch script:
#echo off
setlocal enabledelayedexpansion
for %%f in ("Y:\Samples\Test Videos\*.mkv") do (
set "filename=%%~nxf"
ffmpeg -i "%%f" -vcodec copy -acodec copy -scodec copy -vbsf h264_changesps=level=40 -vbsf h264_changesps=fps=24000:1001 "E:\Converted\!filename!"
)
pause
This works fine for all files without the ! in the name. If I use a batch script like this:
#echo off
for %%f in ("Y:\Samples\Test Videos\*.mkv") do (
call :command "%%f"
)
pause
:command
set "fname=%~nx1"
set "fpath=%~dpnx1"
echo %fname%
ffmpeg -i %fpath% -vcodec copy -acodec copy -scodec copy -vbsf h264_changesps=level=40 -vbsf h264_changesps=fps=24000:1001 "E:\Converted\%fname%"
Then I can get the file names and full paths to echo with the ! included, but the ffmpeg command referencing %fname% and %fpath% returns a No such file or directory message. If I add in SetLocal EnableDelayedExpansion just after the echo off and change the ffmpeg string to have ! instead of %, then I again get the No such file or directory message.
I know that in a FOR loop everything in the DO part of the statement gets expanded before everything else which is why I need to use delayed expansion, but how can I use that and still use file names that contain !? Is there a way to get the first batch to accept files that have the ! in the name?

The simplest solution is not to use delayed expansion.
#echo off
setlocal enableextensions disabledelayedexpansion
set ffmpegArguments=-vcodec copy -acodec copy -scodec copy -vbsf h264_changesps=level=40 -vbsf h264_changesps=fps=24000:1001
for %%f in ("Y:\Samples\Test Videos\*.mkv") do (
ffmpeg -i "%%f" %ffmpegArguments% "E:\Converted\%%~nxf"
)
pause
There is not any need to assign the information retrieved from the for replaceable parameter to a variable, just use the retrieved value

:command
SETLOCAL DISABLEDELAYEDEXPANSION
set "fname=%~nx1"
set "fpath=%~dpnx1"
echo %fname%
ffmpeg -i %fpath% -vcodec copy -acodec copy -scodec copy -vbsf h264_changesps=level=40 -vbsf h264_changesps=fps=24000:1001 "E:\Converted\%fname%"
ENDLOCAL
GOTO :EOF
should fix your problem by disabling the delayedexpansion while ! is a literal character.
I add the goto out of habit so I don't have to remember about it if I add a new subroutine.

I posted too soon! I finally got it working!
It turns out that in the second script using %fpath% for the ffmpeg input only gets the filepath up until the first space where it truncates (because I didn't have it in quotes). After putting %fpath% in quotes, the script works with files that have ! in the name! Here it is:
#echo off
for %%f in ("Y:\Samples\Test Videos\*.mkv") do (
call :command "%%f"
)
:command
set "fname=%~nx1"
set "fpath=%~dpnx1"
ffmpeg -i "%fpath%" -vcodec copy -acodec copy -scodec copy -vbsf h264_changesps=level=40 -vbsf h264_changesps=fps=24000:1001 "E:\Converted\%fname%"

Related

FFmpeg - Check if folder contains a matching filename with 2 different extensions and ignore both files. Process only filenames with 1 extension

I need to batch convert all mkv files in a folder recursively to mp4.
If a filename exists and matches both extensions, ignore both files and process only filenames that contain mkv, without matching mp4.
Example: cat.mkv exists in folder with cat.mp4 = ignore both files
Example: cat.mkv exists in folder and cat.mp4 does not = process cat.mkv to cat.mp4
I have included a script that doesn't work well. It processes all mkv files and mp4 files. The mp4 files throw an error as FFmpeg will not encode the same format in this manner over itself.
As always thank you to anyone who might have a few ideas.
UPDATE: I may have gotten it to work. I changed a few things from the original. If anyone has success or an idea to improve I'm all ears. Thanks.
VERSION 2
#ECHO ON
SETLOCAL
PROMPT $G
COLOR 0A
REM Set FFmpeg.exe location if not in system PATH already
SET FF=C:\MAB\local64\bin-video\ffmpeg.exe
REM Set MKV files root folder to recursively search
SET "mkvPATH=C:\Encoding\1_Original\Test\"
REM Change into mkvPATH DIR
CD "C:\Encoding\1_Original\Test"
REM Set temp file name
SET TEMPFILE=convert_mkv.bat
REM Create empty convert file
COPY NUL "%TEMPFILE%" >NUL 2>&1
REM ADD #ECHO OFF to top of blank convert_mkv.bat script
ECHO #ECHO OFF >>"%TEMPFILE%"
REM Recursively search MKV root folder
FOR /R "%mkvPATH%" %%G IN (*.mkv *.mp4) DO (
SET "GPATH=%%~fG"
SET "GNAME=%%~nG"
SETLOCAL ENABLEDELAYEDEXPANSION
REM Ignore all files that have both
REM extensions ".mkv" and ".mp4" in the file name
IF "%%~nG.mkv"=="%%~nG.mkv" (
IF NOT EXIST "%%~nG.mp4" (
CALL :DO_FFmpeg "!GPATH!"
IF "%%~nG.mkv"=="%%~nG.mkv" (
IF EXIST "%%~nG.mp4" (
ECHO(>>"%TEMPFILE%"
) ELSE ENDLOCAL
)
)
)
)
GOTO END
REM CALL variables for use in FFmpeg's command line
:DO_FFmpeg
IF "%~1"=="" GOTO :END
FOR %%I IN ("%~1") DO (
SET "FOLDER=%%~dpI"
SET "NAME=%%~nxI"
)
REM Export info to "%TEMPFILE% and RUN ffmpeg.exe's command line in the cmd.exe window
ECHO %FF% -y -i "%~1" -ss 0 -t 300 -codec copy "%FOLDER%%~n1.mp4">>"%TEMPFILE%" && %FF% | %FF% -y -i "%~1" -ss 600 -t 30 -codec copy "%FOLDER%%~n1.mp4"
:END
PAUSE
EXIT /B

Why variables set in for loops do not exist

I write two for loops to do data automation. While variables echoed well in each loop, the last step (data process using a well-written batch) keeps giving errors that variables set previous do not exist.
The code loops through the subfolders (q1, q2, etc.) under the directory. For each subfolder, there is another for loop to set several variables. I echoed three variables fine in loops.
However, when using a batch called abc.rb, the error is COM_M does not exist.
Actually, the error is all three variables do not exist.
setlocal ENABLEDELAYEDEXPANSION
for /f %%f in ('dir /ad /b ') do (
echo %%f
pause
pushd %%f
for %%a in (*.a*.dat) do (
set COM_DATA=%%a
echo !COM_DATA!
set COM_V=%%f\com-v.dat
echo !COM_V!
set COM_M=%%f\com-M.dat
echo !COM_M!
)
chdir
set fig=someA
set matrix=someB
rem use a written batch (called abc.rb) to process data
abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
popd
)
endlocal
Can anyone find any bugs? Thank you!
I am not sure why the need to pushd into the dir, but as far as I can see, there is only a need for a single for loop:
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat
echo !COM_V!
set COM_M=%%~dpacom-M.dat
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
)
If you require the pushd (which I doubt)
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
pushd "%%~dpa"
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat"
echo !COM_V!
set "COM_M=%%~dpacom-M.dat"
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
popd
)
The double quotes will help if the paths have whitespace, if your program has an issue with them, then you can remove them: abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "fig=someA"
set "matrix=someB"
set "COM_V=com-v.dat"
set "COM_M=com-M.dat"
for /f %%f in ('dir /ad /b') do (
echo %%f
pause
if exist "%%~f\*.a*.dat" (
pushd "%%~f" && (
for %%a in (*.a*.dat) do (
set "COM_DATA=%%~a"
echo !COM_DATA!
)
chdir
rem use a written batch called abc.rb to process data
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d "!fig!" -e "!matrix!"
popd
)
)
)
endlocal
Issues:
If the nested for loop finds no files with the matched pattern of *.a*.dat, then the variables COM_DATA, COM_V and COM_M may not be defined or updated with a newer value.
Value of COM_DATA is a filename. Values of COM_V and COM_M is the parent folder name and filename, which is inconsistent. Based on the current directory, I would consider filenames as correct. This means that COM_V and COM_M never need to change.
If abc.rb is a batch-file, then you need to use call for the interpreter to return control back to the main script.
Changes:
Test if the file pattern exists, and run the code within the code block if true.
COM_V and COM_M moved out of the for loop as values never change.
Calling abc.rb as being a batch-file.
fig and matrix moved out of the for loop as values never change.
Double quote setting of variables and use of variables to avoid issues with spaces, special characters etc.
pushd && ( ensures the code within the parentheses is run only on success of changing directory.
Removed parentheses in the rem line. They may not cause a problem, though rem lines are parsed and can cause a syntax error. Suggest avoiding special characters in rem lines unless you intend to debug.

drag and drop batch video conversion

I'm using ffmpeg to merge videos, I have a bunch *.mp4 and *.m4a with the same name, the manual way to do the conversion is to type the ffmpeg command with the quoted name of the files, I tried to automate and drag and drop both files to merge but I've not being able to pass the file names to variables and then to ffmpeg, I tried this code with quotes, with ! instead of % but no luck, someone know what I'm doing wrong?
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "params=!cmdcmdline!"
set "params=!params:~0,-1!"
set "params=!params:*" =!"
set count=0
for %%G IN (!params!) do (
if %%~xG==.m4a (
set myaudio=%%~G)
if %%~xG==.mp4 (
set myvideo=%%~G )
)
C:\ffmpeg\bin\ffmpeg.exe -y -i "%myaudio%" -i "%myvideo%" -c copy "%myvideo%_new.mp4"
pause
exit
edit: I had wrong syntaxis, as stated on the comments, removing the extra spaces solved the problem

Batch File: syntax is incorrect while setting variables

I am getting syntax error while seting variables . Can some one please tell me where i am doing wrong.
#echo off
setlocal EnableDelayedExpansion
cd C:\data
for %%i in (*.pgp)
do
(
set encrypted=%%i
set decrypted=!encrypted:.gpg=!
gpg --batch --yes --passphrase "xyz" -o !decrypted! --decrypt !encrypted!
)
endlocal
if i do the same logic with out seting any variables it works
for %%i in (*.pgp)
do
(
must be coded as
for %%i in (*.pgp) do (
ie. the do and the ) and the ( after the do must all be on the same physical line.
Also, in your replace set, have you specified .gpg in place of .pgp?? (in which case, %%~ni could be used in place of the substitution.
in the gpg line, perhaps you need to quote the decrypted and encrypted strings, or you could possibly use "%%~ni" and "%%i" respectively.

Trying to use a batch file to work with filenames that have a forbidden character

Okay, a brief explanation of what I am doing: I use Windows Media Center (Windows 7) to record Jeopardy every evening. I then use Handbrake to convert the .wtv files to .mkv files and then transfer them to my NAS so I can watch them later using Plex Media Server/Center. Rather than doing this "by hand", I'm trying to automate the process using a batch file as a scheduled task. Initially, I had set up a script so that I could right-click > Send To > convert.bat and it would initiate the command-line interface for Handbrake and convert the file, move the output to my NAS, and delete the original file (worked great).
Now, what I'm doing is initiating the batch script as a scheduled task and looping through the contents of my "recorded tv" directory and looping through any .wtv files to convert/move/delete them.
The problem lies in the fact that Windows Media Center correctly names the Jeopardy files with the "!" in them (Eg: Jeopardy!_KHQ_2012_12_04_21_12_12.wtv), which completely bricks my script. The "Send To" batch file worked great, but when I loop through the *.wtv files in the directory, it returns all the filenames with the "!" stripped out which means I can't do squat with them. Files without "!" do process without a hitch.
Thanks in advance to anyone who can get me pointed in the right direction! (and if you happen to see any other areas where this script could be improved, that's fine too...)
Here is the basic code that I am attempting to use:
#echo off
setlocal ENABLEDELAYEDEXPANSION
SET count=0
SET getFolder=C:\Users\Public\Recorded TV\
SET ripFolder=C:\Rips\
SET putFolder=Z:\Videos\Recorded TV\
FOR %%F IN ("%getFolder%*.wtv") DO (
SET /A count=!count!+1
REM DETERMINE OUTPUT FILENAME
for /f "tokens=5,6,7,8,9,10 delims=\_" %%a in ("%%F") do (
set show=%%a
set station=%%b
set year=%%c
set month=%%d
set day=%%e
set hour=%%f
REM GENERATE OUTPUT NAMING CONVENTION
set output=!show! s!year!e!month!!day! !hour!
)
REM PROCESS WITH HANDBRAKE CLI
"C:\Program Files\Handbrake\HandBrakeCLI.exe" -i "%%F" -t 1 -c 1 -o %ripFolder%!OUTPUT!.mkv -f mkv --deinterlace="fast" --crop 58:60:2:2 --strict-anamorphic -e x264 -q 20 --vfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0 --gain=0 --audio-copy-mask none --audio-fallback ffac3 -x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0:8x8dct=0 --verbose=1
REM MOVE CONVERTED FILE TO NAS
copy "%ripFolder%!OUTPUT!.mkv" "%putFolder%"
REM DELETE ORIGINAL
del "%%F"
REM DELETE LOCAL RIP
del "%ripFolder%!output!.mkv"
)
echo %count% files processed
pause
ENDLOCAL
As you have recognized, the exclamation mark is stripped before you can escape it.
That's because you expand the FOR-loop variable %%F while delayed expansion is enabled, and the exclamation mark tries to expand a variable.
You need to toggle the delayed expansion here, as the variable contents are safe when using with delayed expansion, but to get the value you need the disabled mode.
#echo off
setlocal DisableDelayedExpansion
SET count=0
FOR %%F IN ("%getFolder%*.wtv") DO (
set "orgFile=%%F"
SET /A count+=1
REM DETERMINE OUTPUT FILENAME
for /f "tokens=5,6,7,8,9,10 delims=\_" %%a in ("%%F") do (
set show=%%a
set station=%%b
set year=%%c
set month=%%d
set day=%%e
set hour=%%f
setlocal EnableDelayedExpansion
REM GENERATE OUTPUT NAMING CONVENTION
set output=!show! s!year!e!month!!day! !hour!
)
REM PROCESS WITH HANDBRAKE CLI
"C:\Program Files\Handbrake\HandBrakeCLI.exe" -i "%%F" -t 1 -c 1 -o %ripFolder%!OUTPUT!.mkv -f mkv --deinterlace="fast" --crop 58:60:2:2 --strict-anamorphic -e x264 -q 20 --vfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0 --gain=0 --audio-copy-mask none --audio-fallback ffac3 -x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0:8x8dct=0 --verbose=1
REM MOVE CONVERTED FILE TO NAS
copy "%ripFolder%!OUTPUT!.mkv" "%putFolder%"
REM DELETE ORIGINAL
del "!orgFile!"
REM DELETE LOCAL RIP
del "%ripFolder%!output!.mkv"
endlocal
)

Resources