Subtract a specific string from a variable in a for loop - batch-file

I am making a file converter with batch using FFMPEG.
I have encountered a problem when trying to subtract the name of a file from the complete link to the file to get the directory of the folder it is in so after the file is converted it can be put into that directory.
Can someone advise me on how I could subtract the string from the filename variable from the string in the directory
My code:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set filetype=.flac
for /R %%a in ("*%filetype%*") do (
set directory=%%a
set filename=%%~na%filetype%
set convdir=!directory:%filename%=!
echo !convdir!
pause
ffmpeg -i "%%a" "convdir%%~na.wav"
echo Converted %%a
)
echo Convertion Done!
pause

Here's a quick, untested, example of how I'd suggest your task is performed:
#Echo Off
SetLocal EnableExtensions
Set "TreeRoot=."
Set "FileGlob=*.flac"
Set "ConvExtn=.wav"
For /F "Delims=" %%G In (
'"(Set PATHEXT=) & "%__APPDIR__%where.exe" /F /R "%TreeBase%" "%FileGlob%" 2> NUL"'
) Do (
Echo Converting %%~G to %%~nG%ConvExtn%
"ffmpeg.exe" -i %%G "%%~dpnG%ConvExtn%"
If Not ErrorLevel 1 Echo Conversion completed.
)
"%__APPDIR__%timeout.exe" /T 5 1> NUL
Simply adjust the variable values on lines 3 to 5 as necessary. I've used . as the tree root as that is the current directory. Please note however, that your code does not define the current directory, so if this is supposed to be rooted at the location of the batch file itself, you should probably change that to %~dp0..

Related

Find a file within a for loop in command prompt

I am attempting to find files to run commands against in a command prompt (batch) script. So far, so good:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R %%I IN ("*.Marker.txt") DO (
SET MARKER=%%I
SET LOG= ???
ECHO MARKER IS !MARKER! AND LOG IS !LOG!
)
Basically, I want to iterate on a pair of files and execute a command on them (for now just echo). The problem I have is that the log file can be in any directory from where the *.Marker.txt file exists. I can't simply just say 100.Marker.log because that's just the filename, not the full path. Some examples:
C:\Temp\Samples\3A5\100.Marker.txt
C:\Temp\Samples\3A5\9\1\100.Marker.log
C:\Temp\Samples\39B\122.Marker.txt
C:\Temp\Samples\39B\2\5\122.Marker.log
So, not really sure what to do here.
You can use PUSHD/POPD to push/pop the working directory during the script and an inner loop.
FOR /R %%I IN ("*.Marker.txt") DO (
SET DIRECTORY=%%~DPI
SET FILENAME=%%~NI
SET MARKER=%%I
PUSHD !DIRECTORY!
FOR /R %%J IN ("!FILENAME!.log") DO (
SET LOGFILE=%%J
ECHO !MARKER!
ECHO !LOGFILE!
)
POPD
)
Not sure how to find a single value, the FOR technically will run on all matches, but if you say you should only have a single value than the inner loop will only run once per marker file. Maybe there's a more efficient way to search for a single file without using FOR?
If all of your files have the same format (text.Marker.ext), then you can use:
#echo off
setlocal EnableDelayedExpansion
for /R %%A IN ("*.Marker.*") DO (
set MARKER=%%A
for /F "tokens=1 delims=." %%B IN ("%%A") do (
set LOG=%%B
)
echo MARKER IS !MARKER! AND LOG IS !LOG!
)
Please note that I don't generally suggest capital letters in batch file.
We make a loop that loops through all subfolders (for /R) trying to find files containing .Marker. (IN ("*.Marker.*")).
We set the output to a variable (MARKER).
Now, we loop through filename found setting delims to .. This means that: token1.token2.token3. . won't be parsed as a token.
Set result to variable.
#echo off
setlocal enabledelayedexpansion
REM following lines to create test environment
md C:\Temp\Samples\3A5\9\1 2>nul
md C:\Temp\Samples\39B\2\5 2>nul
break>C:\Temp\Samples\3A5\100.Marker.txt
break>C:\Temp\Samples\3A5\9\1\100.Marker.log
break>C:\Temp\Samples\39B\122.Marker.txt
break>C:\Temp\Samples\39B\2\5\122.Marker.log
REM end creating test environment
for /R "C:\Temp\Samples\" %%a in ("*.marker.txt") do (
for /f %%b in ('dir /s/b "%%~dpa\%%~na.log"') do (
ECHO Textfile: %%a Logfile: %%b
)
)
The for /R looks recursively for all matching files (*.marker.txt in C:\Temp\Samples\ and subfolders.
The for /f looks for matching log files below the folder, where the marker.txt is.
Output:
Textfile: C:\Temp\Samples\39B\122.Marker.txt Logfile: C:\temp\Samples\39B\2\5\122.Marker.log
Textfile: C:\Temp\Samples\3A5\100.Marker.txt Logfile: C:\temp\Samples\3A5\9\1\100.Marker.log
Note: this assumes, there is only one matching .log for each .txt and it's within the tree below .txt

Batch file to copy and paste a file as many times as many files there are inside a list of folders

I'm working on a project which works like that:
take a long video hard subtitled and cut it in a lot of smaller
scenes and where each scene lasts the time that it lasts the
subtitle to disappear
cut one only picture (maybe the first one or the last one) of each
segment of the original video and do it automatically
repeat (copy and paste) the picture extracted as many times as the
number of pictures that there are on each fragment that has been cut
on the original video
join everything
I have already achieved the point 1) and 2), now I'm trying to achieve point 3) and I need your help, please. Let's say that I have achieved point 2) with this batch script (that I call a.bat) :
pushd %1
if not exist newfiles\ (
mkdir newfiles
)
if not exist newfiles2\ (
mkdir newfiles2
)
:start
for %%F in (*.mov) do (
md "%%~nF"
echo "%%~nF"
ffmpeg -i %%F -r 1 -f image2 -qscale:v 2 "%%~nF\%%~nF_image-%%3d.png"
copy "%%~nF\%%~nF_image-001.png" ".\newfiles"
)
popd
So, I have this situation:
It means that in each folder there is a variable number of images that have been extracted from the *.mov files. Now, I want that the first image on the list of the images located on the relative folders is copied in another folder, as many times as many files there are in the same folder where it is. For example, let's take a look at this folder:
Here there are 4 images, right? I want to copy the first (or the last) image of the list for 4 times in this folder:
I would like that someone can help me to improve this batch script (that I call b.bat)...
#echo off
setlocal enabledelayedexpansion
set count=0
for %%x in (*.png) do set /a count+=1
echo %count%
FOR %%F IN (*.png) DO (
set filename=%%F
goto tests
)
:tests
echo "%filename%"
for /l %%i in (1,1,%count%) do copy %filename% ..\newfiles2\%%i.png
endlocal
pause
because I figured out that it works only if I put a copy of it in every/each folder,but then I have to enter in each folder to run it,wasting a lot of time. Let's say that I want to run it in the folder where all the others folders are located. How can I modify the script to do so ? This is what happens if I run it on the "root" folder :
EDIT : thanks to the suggestion of #AlexP I modified the script like this :
#echo off
setlocal enabledelayedexpansion
set count=0
for /d %%d in (render*) do set /a count+=1t
echo %count%
pause
for %%f IN ("%%~d\*") do (
set filename=%%F
goto tests
)
:tests
echo "%filename%"
for /l %%i in (1,1,%count%) do copy %filename% .\%%i.png
endlocal
pause
now the var "count" has value 4,but this is wrong. the value that i need is inside each folder that starts with the word render* ; so,since I have 4 pics inside the "render00003211" folder the value is 4; 5 pics under "render00003266" the value is 5 and so on..
UPDATE : I've ordered sequentially the names of the pictures by placing this script inside the Duplicates folder...
#echo off
setlocal enabledelayedexpansion
mkdir pics
copy *.png .\pics
del *.png
for /d /r %%a in (%1\*.*) do (
set /a counter=0
for %%b in ("%%a\*.*") do (
set /a counter += 1
set zcounter=0000!counter!
set source="%%~fb"
set target="pic!zcounter:~-4!%%~xb"
echo Renaming !source! to !target!
ren !source! !target!
)
)
for /l %%i in (1,1,%count%) do copy %filename% %somewhere%
From for /?:
FOR /L %variable IN (start,step,end) DO command [command-parameters]
The set is a sequence of numbers from start to end, by step amount. So (1,1,5) would generate the sequence (1 2 3 4 5) and (5,-1,1) would generate the sequence (5 4 3 2 1).
Here is a script which implements what I understand to be the requirements:
#echo off
:: This script will visit each subdirectory of the directory from where it is
:: run; it will take the first file found in that subdirectory and copy it to
:: the directory ..\Duplicates as many times as there are files in that
:: subdirectory; the names of the copied files are taken from the name of the
:: subdirectory with a numeric suffix appended, and have the extension of the
:: copied file. If ..\Duplicates does not exist then it is created.
setlocal
echo Working in directory "%CD%"
if not exist ..\Duplicates mkdir ..\Duplicates
if not exist ..\Duplicates\. (
echo Destination ..\Duplicates does not exist, and I cannot create it.
exit /b
)
for /d %%d in (*) do call :duplicateDir "%%~d"
exit /b
:duplicateDir
echo * "%~1"
set COUNT=0
set "SUBNAME=%~n1"
set SUBFILE=&
set SUBEXT=&
for %%f in ("%~1\*") do call :duplicateFile "%%~f"
echo "%SUBFILE%" copied %COUNT% times
exit /b
:duplicateFile
if not defined SUBFILE set "SUBFILE=%~1"
if not defined SUBEXT set "SUBEXT=%~x1"
set /a COUNT=COUNT+1
echo copy "%SUBFILE%" "..\Duplicates\%SUBNAME%-%COUNT%%SUBEXT%"
:: copy "%SUBFILE%" "..\Duplicates\%SUBNAME%-%COUNT%%SUBEXT%" >nul
exit /b
Note that instead of actually doing the copying the script displays the command which would be executed. When you are happy with it, comment out the echo copy and uncomment the copy.

How to copy a file with a new name with parts from original file name?

I have a bunch of files with the name 383DT_SBY_20170420_08_C.ps, 380_DB_20170421_08_C.ps, etc.
I am trying to create a script that will automatically copy and rename them to SBY_20170420_08.ps, DB_20170421_08.ps.
The following script used to work but now it tells me it can't find the file specified.
#echo off
T:
cd \PROOFS\out\
for /f "tokens=1,2,3,4,5 delims=_ " %%a in ("%1") do set first=%%a&set second=%%b&set third=%%c&set fourth=%%d&set fifth=%%e
copy %1 Renamed\"%second%%third%_%fourth%.ps"
test the existence of the file prior trying to copy it.
if you only need tokens 2 to 4, there is no need to put 1 and 5 into vars
if a (mapped) drive isn't available the seperate drive change and cd don't work, better use CD /D or Pushd and check success
the batch depends on the passed arguments if the arg doesn't match with *_*_*_*_*.ps it will fail
if the batch shall handle all files with the pattern you need an additional for loop
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
CD /D "T:\PROOFS\out\" || pause & goto :Eof
for /f "tokens=2-4 delims=_" %%a in ("%~1") do If exist "%~1" (
copy "%~1" "Renamed\%%a_%%b_%%c%~x1"
) else (
Echo %~1 doesn*t exist in %CD%
)

Batch script, file drag&drop, change extension and reload file

I have a batch script which accepts >=1 files via drag & drop. The full paths are kept in an array and later fed to another program as input. The file name+extension is kept in another array which is then shown to the user.
I am trying to check the file extension and if it is not .bin, automatically rename the file I dragged & dropped to .bin and reload it to be added to the two arrays. How can I do that? I have tried some if commands or %%~xi but usually it doesn't properly detect if it's .bin or not and of course the path is not updated at the array.
#echo off
pushd %~dp0
setlocal enabledelayedexpansion
set /a Count = 0
:START
set file="%~1"
if %file%=="" goto RUN
for %%i in (%file%) do set name=%%~nxi
set /a Count += 1
set [FilePath.!Count!]=%file%
set [FileName.!Count!]=%name%
shift
goto START
:RUN
for /l %%i in (1, 1, %Count%) do (
program.exe -command "![FilePath.%%i]!"
echo.
echo File Name: ![FileName.%%i]!
echo.
pause
cls
if exist file.log del file.log
)
You could just rename it during your array population. There are a few other potential problems with your script (such as pushd to an unquoted directory, delayedexpansion where you don't need it where it will potentially clobber filenames containing exclamation marks, and if %file%=="" should have "%file%" quoted). And there's really no reason to maintain an array of filenames or loop twice. Your script is much more complicated than it needs to be.
#echo off
setlocal
pushd "%~dp0"
for %%I in (%*) do (
if /I not "%%~xI"==".bin" move "%%~fI" "%%~dpnI.bin" >NUL
program.exe -command "%%~dpnI.bin"
echo;
echo File Name: "%%~nxI"
echo;
rem If you want to rename the file back the way it was after running
rem program.exe on it, uncomment the following line:
rem if /I not "%%~xI"==".bin" move "%%~dpnI.bin" "%%~fI" >NUL
pause
cls
if exist file.log del file.log
)

Batch File Loop Skip File if name contains

I am creating this batch file, that works with handbrakecli, to batch convert avi to mp4.
However I am stuck in how to continue the loop and skip the current file inside a loop.
FOR /R "%somepath%" %%G in (*.avi) DO (
rem skip if filename contains word trailer
rem skip if file name contains word sample
rem do conversion
)
This currently doesn't work in skipping the files that contain trailer or sample
I have tried using find or findstr and both fail to skip.
echo "%%G" | c:\windows\system32\findstr /i "trailer" > NUL
If %ERRORLEVEL% EQU 1 set skip Yes
Here is for sample.
echo "%%G" | c:\windows\system32\findstr /i "sample" > NUL
If %ERRORLEVEL% EQU 1 set skip Yes
If a file contains either trailer or sample, I do not want to do any handbrakecli conversions, but to just skip it.
I do echo's to display which files get converted, and it does include files with Sample or sample in the name.
I have tried using find or findstr and both fail to set skip to yes
if skip == No do ( rem do conversion )
I only want to convert non-trailer/sample avi files.
Thank you for your time.
try this, put your conversion commands in the loop and remove the word echo before handbrakecli if the output is OK:
#echo off &setlocal
FOR /R "%somepath%" %%G in (*.avi) DO (
set "fpath=%%G"
set "fname=%%~nG"
setlocal enabledelayedexpansion
if "!fname!"=="!fname:trailer=!" if "!fname!"=="!fname:sample=!" (
echo handbrakecli.exe "!fpath!" &rem put your conversion command here
>>"logfile.log" echo !fname!
)
endlocal
)
The file name+file path is in the variable "!fpath!".
Added some code concerning the needs of the OP:
#echo off &setlocal
rem replace avi with mp4 files in my movie folder
rem grab 4 random folders with avi in them and no mp4
rem Settings for this Batch File
set "moviepath=H:\Movies"
set "logfile=C:\Documents and Settings\%USERNAME%\LogFiles\avi_converter.log"
rem check if log file exists
if not exist "%logfile%" echo(>"%logfile%"
rem create empty convert file
copy nul "convert_movies.bat" >nul 2>&1
rem add echo off
echo #echo off >>"convert_movies.bat"
rem set counter
SET /A COUNT=1
FOR /R "%moviepath%" %%G in (*.avi) DO (
set "fpath=%%~fG"
set "fname=%%~nG"
setlocal enabledelayedexpansion
rem check if count greater than 4
if !COUNT! gtr 4 goto:eof
if "!fname!"=="!fname:trailer=!" if "!fname!"=="!fname:sample=!" (
rem echo handbrakecli.exe "!fpath!" &rem put your conversion command here
rem Send File To HandBrakeCLI
CALL :DOHandBrakeCLI "!fpath!"
rem Delete File
CALL :DeleteOldFile "!fpath!"
rem Add Log Entry
CALL :LogEntry "!fpath!"
rem add line break space
echo( >>"convert_movies.bat"
endlocal
rem increment counter
SET /A COUNT+=1
) else endlocal
)
rem end main program, to close cmd window replace it with EXIT
goto:eof
:DOHandBrakeCLI
rem skip if the parameter is empty
IF "%~1"=="" goto:eof
For %%A in ("%~1") do (
Set "Folder=%%~dpA"
Set "Name=%%~nxA"
)
rem echo %Folder%%Name%
echo start /b "" "c:\handbrakecli\HandBrakeCLI.exe" -i "%~1" -o "%Folder%%~n1.mp4" --preset="High Profile">>"convert_movies.bat"
exit /b
:DeleteOldFile
rem skip if the parameter is empty
IF "%~1"=="" goto:eof
For %%A in ("%~1") do (
Set "Folder=%%~dpA"
Set "Name=%%~nxA"
)
rem sends parameters to deletefile which will make sure new file exists before deleting old one
echo c:\projects\deletefile.bat "%~1" "%Folder%%~n1.mp4">>"convert_movies.bat"
exit /b
:LogEntry
rem skip if the parameter is empty
IF "%~1"=="" goto:eof
echo "%~1">>"%logfile%"
exit /b
This should work:
#echo off
FOR /R "%somepath%" %%G in (*.avi) DO (
echo "%%~nG" |findstr /i "trailer sample">nul || (
rem do conversion
)
)
It's difficult to see where your post is pseudocode and where actual code.
The first sample contains only REM statements, so it's not surprising it apparently does nothing.
Your second and third sample are effectively identical - the only difference is the target string. It's not surprising that the variable skip isn't set to Yes since the correct syntax is
if %errorlevel% equ 0 set skip=Yes
The syntax you've posted will REPORT that skip is not defined - it ignores the Yes
HOWEVER this syntax is only usable OUTSIDE of a "block statement" - that is, a multiple-instruction statement (enclosed in parentheses) or cascaded&by&ampersands. Batch first PARSES a complete statement - from the FOR or if through to the appropriate closing-parenthesis and THEN executes it. As part of the PARSING phase, any %var% - including %errorlevel% is replaced by its value as it stands at the time the entire statement is parsed - not as it changes due to the operation of the for.
In order to use the value as it changes, you need to use
if errorlevel 1 (do_something) else (do_something_else)
where do_something and do_something_else) may themselves be compound statements.
OR
if defined variable (do_something) else (do_something_else)
where the variable either is defined or not
OR
setlocal enabledelayedexpansion
....
if !errorlevel! equ x (do_something) else (do_something_else)
OR
if !var! neq something (do_something) else (do_something_else)
But it's quite possible that
FOR /R "%somepath%" %%G in (*.avi) DO (
echo(%%G|findstr /i "sample trailer" >nul
if errorlevel 1 echo %%G
)
will give you an appropriate skeleton.
Echo the filename through FINDSTR and look for "sample" or "trailer" /i case-insensitive. Findstr sets errorlevel 0 if either target string is found, 1 otherwise - and the if errorlevel x syntax works on the dynamic value of errorlevel within a loop.
#ECHO on &SETLOCAL
REM This script was inspired by Endoro's expanded script
(https://stackoverflow.com/a/16891696/10572786).
REM This batch script will recursively search for all .mp4 files that don't
have (x265) in the file name. Any valid results will be encoded with x265
using FFmpeg. The original .mp4 file will remain unchanged in it's original
folder with the new x265 version.
REM Example: %PATH%\A.mp4 > %PATH%\A(x265).mp4
REM If you don't have ffmpeg.exe on your PC you must download or build it
with Microsoft Visual Studios. I recommend you download and run media
autobuild suite on GitHub: (https://github.com/jb-alvarado/media-
autobuild_suite).
REM Once ffmpeg is compiled/downloaded make sure to set it's folder path as
an environmental variable in Windows before running the script. Change the
script's working directory to your .mp4 files root folder using the "cd"
command.
REM !!BEGIN SCRIPT!!
cd /d %USERPROFILE%\Desktop\Vids\
REM or perhaps use [cd /d %OneDrive%\Desktop\Vids]
REM Set mp4PATH to the root folder you wish to recursively search.
SET "mp4PATH=%USERPROFILE%\Desktop\Vids\"
REM Create empty convert file.
COPY NUL "convert_movies.bat" >NUL 2>&1
REM Add ECHO off.
ECHO #ECHO off >>"convert_movies.bat"
REM Recursively search root folder.
FOR /R "%mp4PATH%" %%G IN (*.mp4) DO (
SET "fpath=%%~fG"
SET "fname=%%~nG"
SETLOCAL enabledelayedexpansion
REM Ignore all files that have "(x265)" in the file name.
IF "!fname!"=="!fname:*(x265)=!" (
CALL :DO_FFmpeg_CLI "!fpath!"
ECHO(>>"convert_movies.bat"
) ELSE ENDLOCAL
)
)
GOTO:EOF
REM CALL variables for use in FFmpeg's command line.
:DO_FFmpeg_CLI
IF "%~1"=="" GOTO:EOF
FOR %%I IN ("%~1") DO (
SET "Folder=%%~dpI"
SET "Name=%%~nxI"
)
REM Export info to "convert_movies.bat and run ffmpeg.exe's command line in the cmd.exe window.
ECHO ffmpeg -y -i "%~1" -c:v libx265 -preset slow -crf
18 -c:a aac "%Folder%%~n1(x265).mp4">>"convert_movies.bat" && ffmpeg |
ffmpeg -y -i "%~1" -c:v libx265 -preset slow
-crf 18 -c:a aac "%Folder%%~n1(x265).mp4"
EXIT /B
PAUSE
The Batch file below solve the original question AND limit the number of converted files to a given number (that does not appear in the original question):
#echo off
setlocal EnableDelayedExpansion
rem Insert in the next line the list of files to skip
set skip=/trailer/sample/
set count=0
FOR /R "%somepath%" %%G in (*.avi) DO (
if /I "!skip:/%%~nG/=!" equ "%skip%" (
echo Current file name is not in skip variable
echo Do conversion on: %%G
set /A count+=1
if !count! equ 20 goto :endLoop
)
)
:endLoop
echo Converted files: %count%

Resources