I have about 300 000 files in a directory. They are sequentially numbered - x000001, x000002, ..., x300000. But some of these files are missing and I need to write an output text file containing the missing file numbers. The following code does it only up to 10 000 files:
#echo off
setlocal enabledelayedexpansion
set "log=%cd%\logfile.txt"
for /f "delims=" %%a in ('dir /ad /b /s') do (
pushd "%%a"
for /L %%b in (10000,1,19999) do (
set a=%%b
set a=!a:~-4!
if not exist "*!a!.csv" >>"%log%" echo "%%a - *!a!.csv"
)
popd
)
How to extend it to 3 * 10^5 files?
Solution 1 - simple but slow
If all 300000 CSV files are in current directory on executing the batch file, this batch code would do the job.
#echo off
set "log=%cd%\logfile.txt"
del "%log%" 2>nul
for /L %%N in (1,1,9) do if not exist *00000%%N.csv echo %%N - *00000%%N.csv>>"%log%"
for /L %%N in (10,1,99) do if not exist *0000%%N.csv echo %%N - *0000%%N.csv>>"%log%"
for /L %%N in (100,1,999) do if not exist *000%%N.csv echo %%N - *000%%N.csv>>"%log%"
for /L %%N in (1000,1,9999) do if not exist *00%%N.csv echo %%N - *00%%N.csv>>"%log%"
for /L %%N in (10000,1,99999) do if not exist *0%%N.csv echo %%N - *0%%N.csv>>"%log%"
for /L %%N in (100000,1,300000) do if not exist *%%N.csv echo %%N - *%%N.csv>>"%log%"
set "log="
Solution 2 - faster but more difficult to understand
This second solution is definitely much faster than above as it processes the list of file names in current directory from first file name to last file name.
In case of last file is not x300000.csv, the batch code below just writes one more line into the log file with the information from which number to expected end number 300000 files are missing in current directory.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Delete log file before running file check.
set "log=%cd%\logfile.txt"
del "%log%" 2>nul
rem Define initial value for the number in the file names.
set "Number=0"
rem Define the file extension of the files.
set "Ext=.csv"
rem Define beginning of first file name with number 1.
set "Name=x00000"
rem Define position of dot separating name from extension.
set "DotPos=7"
rem Process list of files matching the pattern of fixed length in current
rem directory sorted by file name line by line. Each file name is compared
rem case-sensitive with the expected file name according to current number.
rem A subroutine is called if current file name is not equal expected one.
for /F "delims=" %%F in ('dir /B /ON x??????%Ext% 2^>nul') do (
set /A Number+=1
if "!Name!!Number!%Ext%" NEQ "%%F" call :CheckDiff "%%F"
)
rem Has last file not expected number 300000, log the file numbers
rem of the files missing in current directory with a single line.
if "%Number%" NEQ "300000" (
set /A Number+=1
echo All files from number !Number! to 300000 are also missing.>>"%log%"
)
endlocal
rem Exit this batch file to jump to predefined label EOF (End Of File).
goto :EOF
rem This is a subroutine called from main loop whenever current file name
rem does not match with expected file name. There are two reasons possible
rem with file names being in expected format:
rem 1. One leading zero must be removed from variable "Name" as number
rem has increased to next higher power of 10, i.e. from 1-9 to 10,
rem from 10-99 to 100, etc.
rem 2. The next file name has really a different number as expected
rem which means there are one or even more files missing in list.
rem The first reason is checked by testing if the dot separating name
rem and extension is at correct position. One zero from end of string
rem of variable "Name" is removed if this is the case and then the
rem new expected file name is compared with the current file name.
rem Is the perhaps newly determined expected file name still not
rem equal the current file name, the expected file name is written
rem into the log file because this file is missing in list.
rem There can be even more files missing up to current file name. Therefore
rem the number is increased and entire subroutine is executed once more as
rem long as expected file name is not equal the current file name.
rem The subroutine is exited with goto :EOF if the expected file name
rem is equal the current file name resulting in continuing in main
rem loop above with checking next file name from directory listing.
:CheckDiff
set "Expected=%Name%%Number%%Ext%"
if "!Expected:~%DotPos%,1!" NEQ "." (
set "Name=%Name:~0,-1%"
set "Expected=!Name!%Number%%Ext%"
)
if "%Expected%" EQU %1 goto :EOF
echo %Expected%>>"%log%"
set /A Number+=1
goto CheckDiff
For understanding the used commands in both solutions 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 /?
dir /?
echo /?
endlocal /?
for /?
if /?
goto /?
rem /?
set /?
setlocal /?
#echo off
setlocal EnableDelayedExpansion
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
set "num=1000000"
del logfile.txt 2> NUL
< NUL (for %%a in (*.csv) do (
set /A num+=1
set /P "=!num:~1!!CR!"
if "x!num:~1!" neq "%%~Na" call :missingFile "%%~Na"
))
goto :EOF
:missingFile file
echo x%num:~1%.csv>> logfile.txt
echo x%num:~1%.csv Missing
set /A num+=1
if "x%num:~1%" neq "%~1" goto missingFile
exit /B
Related
This is more sort of a continuation to this question, I referred to the answer section and its fine but it doesn't seem to work for JSON files. The below is my JSON
{
"id": "ee308826-5aa6-412b-ba65-fd647d9cc8e8",
"name": "Stub",
"request": {
"url": "/World/Wilfred",
"method": "GET"
}
}
I need the file to be renamed with "Stub_GET_World_Wilfred.json" which is of the syntax name_method_url ( without the / )
Script from the Original Question :
#echo off
for %%i in (%1) do (
for /f "tokens=2 delims==" %%j in ('findstr /B /I "Description=" "%%i"') do (
ren "%%i" "%%j.temp_txt"
)
)
ren *.temp_txt *.txt
The following commented batch file could be used for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ErrorCount=0"
set "FileCount=0"
rem Was the batch file called with an argument string to process one file?
if not "%~1" == "" (
rem Is there no file (or directory) with the name passed to the batch file?
if not exist "%~1" (
set "ErrorCount=1"
echo ERROR: File "%~1" does not exist.
) else call :ProcessFile "%~1"
goto EndBatch
)
rem Process all files with the file extension .json in current directory.
for /F "eol=| delims=" %%I in ('dir *.json /A-D /B 2^>nul') do call :ProcessFile "%%I"
goto EndBatch
rem Rund FINDSTR on the file (or wrong on directory) in a background command
rem process started by FOR to find lines with the literal string "url" or the
rem literal string "method" in any case and process this line by splitting it
rem into substrings using comma, colon, horizontal tab and normal space as
rem string delimiters with using first substring without the surrounding quotes
rem as environment variable name and assigning the second substring without the
rem surrounding quotes to the environment variable. Please note that this code
rem does not work for a url with a colon or comma inside.
:ProcessFile
set /A FileCount+=1
set "method="
set "url="
for /F "tokens=1,2 delims=,: " %%J in ('%SystemRoot%\System32\findstr.exe /I /L /C:"\"url\"" /C:"\"method\"" "%~1"') do set "%%~J=%%~K"
if not defined url (
rem There was no string "url" found in the file (or the passed string
rem was a folder name) or the line with "url" has not a string value.
set /A ErrorCount+=1
echo ERROR: Could not find a url in "%~1".
goto :EOF
)
if not defined method (
rem There was no string "method" found in the file (or the passed string
rem was a folder name) or the line with "method" has not a string value.
set /A ErrorCount+=1
echo ERROR: Could not find a method in "%~1".
goto :EOF
)
rem Create the new file name by concatenating Stub_ with the method string
rem and with the url string read from the file with each slash replaced by
rem an underscore in url and file extension .json appended. There is not
rem replaced any percent encoded character in the url by the appropriate
rem character.
set "FileName=Stub_%method%%url:/=_%.json"
rem There is nothing done on name of processed file has case-insensitive
rem compared already the right name.
if /I "%~nx1" == "%FileName%" goto :EOF
rem Next is checked if a file with new name exists already in directory of the
rem file passed to the batch file which makes it impossible to rename the file.
if exist "%~dp1%FileName%" (
set /A ErrorCount+=1
echo ERROR: A file with name "%FileName%" exits already in "%~dp1".
goto :EOF
)
rem Otherwise the file is renamed which can still fail for various reasons.
ren "%~1" "%FileName%"
if errorlevel 1 (
set /A ErrorCount+=1
echo ERROR: File "%~1" could not be renamed to "%FileName%".
goto :EOF
)
echo Renamed file "%~1" to "%FileName%".
goto :EOF
:EndBatch
set "Plural_S_Files="
if not %FileCount% == 1 set "Plural_S_Files=s"
set "Plural_S_Errors="
if not %ErrorCount% == 1 set "Plural_S_Errors=s"
echo/
echo Proccessed %FileCount% file%Plural_S_Files% with %ErrorCount% error%Plural_S_Errors%.
if not %ErrorCount% == 0 echo/& pause
endlocal
ATTENTION: There must be a horizontal tab and a space after delims=,: left to " in the batch file. Please make sure to have only these two characters and not multiple spaces in the batch file after copying and pasting the batch file code.
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 /?
dir /?
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
pause /?
rem /?
ren /?
set /?
setlocal /?
See single line with multiple commands using Windows batch file for an explanation of operator &.
I have a Batch script :
#echo off
setlocal enableDelayedExpansion
SET /P UserInput=Please Enter a Number:
SET /A number=UserInput
ECHO number=%number%
for %%i in (*.jpeg) do call :JPG %%~ni %%i
goto :end
:JPG
set str=%1
set /a str2=%str:_color=%
set /a newnamej=%str2%+%number%
echo %1 ==> I can see the problem with it
set lastnamej=%newnamej%_color.jpeg
ren %2 %lastnamej%
goto :eof
:end
The goal of this script is to take all file in a folder. They are all named after a number (1_color.jpeg, 2_color.jpeg, 3_color.jpeg,..) and I want to rename them with an additionnal number (if user input is 5, 1_color.jpeg will become 6_color.jpeg, and so on).
I have a problem with this script.
if I use a number such as 555, the first file will pass in the for loop 2 times.
(little example : 1_color.jpeg and 2_color.jpeg,
I use my script with 5 so 1_color.jpeg => 6_color.jpeg and 2_color.jpeg => 7_color.jpeg but then, 6_color.jpeg will be read again once, and will become 11_color.jpeg, so my result will be 11_color.jpeg and 7_color.jpeg).
Do someone know how to fix this issue?
Thanks for all!
The problem have two parts: the for %%i in (*.jpeg) ... command may be dinamically affected by the position that a renamed file will occupy in the whole file list, so some files may be renamed twice and, in certain particular cases with many files, up to three times.
The solution is to use a for /F %%i in ('dir /B *.jpeg') ... command instead, that first get the list of all files, and then start the renaming process.
Also, the rename must be done from last file to first one order, to avoid duplicate numbers.
However, in this case the use of for /F combined with "tokens=1* delims=_" option also allows to process the first underscore-separated number in the file names in a simpler way:
#echo off
setlocal EnableDelayedExpansion
SET /P number=Please Enter a Number:
ECHO number=%number%
for /F "tokens=1* delims=_" %%a in ('dir /O:-N /B *.jpeg') do (
set /A newNum=%%a+number
ren "%%a_%%b" "!newNum!_%%b"
)
User Aacini provided a nice solution in his answer, pointing out both issues at hand, namely the fact that for does not fully enumerate the directory in advance (see this thread: At which point does for or for /R enumerate the directory (tree)?) and the flaw in the logic concerning the sort order of the processed files.
However, there is still a problem derived from the purely (reverse-)alphabetic sort order of dir /B /O:-N *.jpeg, which can still cause collisions, as the following example illustrates:
9_color.jpeg
8_color.jpeg
7_color.jpeg
6_color.jpeg
5_color.jpeg
4_color.jpeg
3_color.jpeg
2_color.jpeg
10_color.jpeg
1_color.jpeg
So if the entered number was 1, file 9_color.jpeg is tried to be renamed to 10_color.jpeg, which fails because that file already exists as it has not yet been processed (hence renamed to 11_color.jpeg).
To overcome this problem, you need to correctly sort the items in reverse alpha-numeric order. This can be achieved by left-zero-padding the numbers before sorting them, because then, alphabetic and alpha-numeric sort orders match. Here is a possible implementation:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_LOCATION=." & rem // (directory containing the files to rename)
set "_PATTERN=*_*.jpeg" & rem // (search pattern for the files to rename)
set "_REGEX1=^[0-9][0-9]*_[^_].*\.jpeg$" & rem // (`findstr` filter expression)
set "_TEMPFILE=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (path to temporary file)
rem // Retrieve numeric user input:
set "NUMBER="
set /P NUMBER="Please Enter a number: "
set /A "NUMBER+=0"
if %NUMBER% GTR 0 (set "ORDER=/R") else if %NUMBER% LSS 0 (set "ORDER=") else exit /B
rem /* Write `|`-separated list of left-zero-padded file prefixes, original and new
rem file names into temporary file: */
> "%_TEMPFILE%" (
for /F "tokens=1* delims=_" %%E in ('
dir /B "%_LOCATION%\%_PATTERN%" ^| findstr /I /R /C:"%_REGEX1%"
') do (
set "NAME=%%F"
setlocal EnableDelayedExpansion
set "PADDED=0000000000%%E"
set /A "NUMBER+=%%E"
echo !PADDED:~-10!^|%%E_!NAME!^|!NUMBER!_!NAME!
endlocal
)
)
rem /* Read `|`-separated list from temporary file, sort it by the left-zero-padded
rem prefixes, extract original and new file names and perform actual renaming: */
< "%_TEMPFILE%" (
for /F "tokens=2* delims=|" %%K in ('sort %ORDER%') do (
ECHO ren "%%K" "%%L"
)
)
rem // Clean up temporary file:
del "%_TEMPFILE%"
endlocal
exit /B
After having successfully verified the correct output of the script, to not forget to remove the upper-case ECHO command in front of the ren command line.
The script uses a temporary file that receives a |-separated table with the padded numeric prefix in the first, the original file name in the second and the new file name in the third column, like this:
0000000010|10_color.jpeg|11_color.jpeg
0000000001|1_color.jpeg|2_color.jpeg
0000000002|2_color.jpeg|3_color.jpeg
0000000003|3_color.jpeg|4_color.jpeg
0000000004|4_color.jpeg|5_color.jpeg
0000000005|5_color.jpeg|6_color.jpeg
0000000006|6_color.jpeg|7_color.jpeg
0000000007|7_color.jpeg|8_color.jpeg
0000000008|8_color.jpeg|9_color.jpeg
0000000009|9_color.jpeg|10_color.jpeg
The temporary file is read and sorted by the sort command. The strings from the second and third columns are extracted and passed over to the ren command.
I need to move few files from one folder to sub folder. My folder structure is already ready.
File current folder: D:\AB\*.*
The file name is: SS-AA-Report-Temp File for Script Testing-Daily-31March.txt
Destination folder: D:\AB\Pm 1.1 File For Script\Daily\
How to check file name substring with folder name substring and move?
Note I have multiple files like this.
set Path1= d:\AB
Pushd %Path1%
echo %Path1%
for %%i in (*.*) do SET "FName=%%~ni"
For /F "Tokens=4-5 Delims=-" %%A In ("%FName%") Do (
Set "FoldOne=%%A"
Set "FoldTwo=%%B"
)
echo out %RDate%
mkdir %Path1%\"%FoldOne%"\"%FoldTwo%"\%RDate%
move %Path1%\"%FName%".* %Path1%\"%FoldOne%"\"%FoldTwo%"\%RDate%\
Edit:
File names format:
A-A-Format-Here First connectivity install on Day 0 regurlarly-Daily-All-2017-03-27-09-31-16.xls
A-A-Format-Already First connectivity with 10 days created-Weekly-All-2016-11-28-10-01-02.csv
A-A-Report-withname 1.2 Sample Report (Network Plan Report)-Daily-Detail-2017-01-03-23-53.xls
A-A-Report-Nextreport 1.2 Sample Report (Network Plan Report)-Weekly-Detail-2017-01-03-23-02-53.csv
Now my folder structure is:
D:\AB\Pm 1.1 First connectivity install on Day 0\Daily\05042017
D:\AB\Pm 2.1 First connectivity with 10 days\Weekly\29032017
D:\AB\Pm 1.2 Sample Report\Daily\05042017
D:\AB\Pm 1.2 Sample Report\Weekly\29032017
And here is the batch file I have already:
set Path1= d:\AB
Pushd %Path1%
echo %Path1%
for %%i in (*.*) do SET "FName=%%~ni"
For /F "Tokens=4-5 Delims=-" %%A In ("%FName%") Do (
Set "FoldOne=%%A"
Set "FoldTwo=%%B"
)
echo 1 %FoldOne%
echo 3 %FoldTwo%
IF %FoldTwo% == Daily (
echo here Daily
For /F UseBackQ %%A In (
`PowerShell "(Get-Date).AddDays(-1).ToString('ddMMyyyy')"`
) Do (Set "RDate=%%A"
echo ffor %RDate%
)
)
IF %FoldTwo% == Weekly (
Echo Weekly
For /F UseBackQ %%A In (
`PowerShell "(Get-Date).AddDays(-7).ToString('ddMMyyyy')"`
) Do (Set "RDate=%%A"
echo %RDate%
)
)
mkdir %Path1%\"%FoldOne%"\"%FoldTwo%"\%RDate%
move %Path1%\"%FName%".* %Path1%\"%FoldOne%"\"%FoldTwo%"\%RDate%\
Pushd d:\
GoTo :EOF
The logic for matching file name substrings with folder name is still very fuzzy.
However, I coded two possible solutions doing both the same using partly different methods.
The first complete batch code:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
cd /D "D:\AB"
rem Get name of each subfolder starting with string Pm, a space, two single
rem digit numbers separated by a dot, one more space and more characters to
rem indexed environment variables for later usage. And assign the substring
rem after the first 7 characters of each folder name also to an index
rem environment variable.
set FolderIndex=0
for /D %%I in ("Pm ?.? *") do (
set "FolderName!FolderIndex!=%%I"
set "CurrentPath=%%I
set "FolderPart!FolderIndex!=!CurrentPath:~7!"
set /A FolderIndex+=1
)
set "FolderCount=%FolderIndex%"
rem set Folder
rem Get date of yesterday and date of a week ago.
for /F "usebackq" %%I in (`PowerShell.exe "(Get-Date).AddDays(-1).ToString('ddMMyyyy')"`) do set "DateDaily=%%I"
for /F "usebackq" %%I in (`PowerShell.exe "(Get-Date).AddDays(-7).ToString('ddMMyyyy')"`) do set "DateWeekly=%%I"
rem set Date
rem Process now each file matching the wildcard pattern below in
rem current folder and calling a subroutine with current file name.
set "FileNotMoved=0"
for %%I in (*-*-*-*-*) do call :MoveToFolder "%%I"
endlocal & if not %FileNotMoved% == 0 pause
goto :EOF
rem This subroutine first gets fourth and fifth dash delimited part from
rem each passed double quoted file name.
rem Then it replaces in each fourth file name part each folder name part
rem by an empty string until either all folder name parts are processed
rem or the string substitution was successful meaning the file name part
rem really contains the folder name part.
rem Note: The substitution does not work correct if any folder name part
rem contains an equal sign as this character is the delimiter
rem between string to find and replace string for substitution.
rem In second case with substitution being successful the folder for
rem the file could be determined and the file is moved to the found
rem folder if also time part could be determined from file name.
:MoveToFolder
for /F "tokens=4,5 delims=-" %%A in ("%~1") do set "NamePart=%%A" & set "NameTime=%%B"
set "FolderIndex=0"
:FindFolder
if %FolderIndex% == %FolderCount% (
set "FileNotMoved=1"
echo Found no folder for: %1
goto :EOF
)
call set "CurrentPart=%%FolderPart%FolderIndex%%%"
if "!NamePart:%CurrentPart%=!" == "!NamePart!" (
set /A FolderIndex+=1
goto FindFolder
)
call set "CurrentFolder=%%FolderName%FolderIndex%%%"
if /I "%NameTime%" == "Daily" (
set "FolderTime=%DateDaily%"
) else if /I "%NameTime%" == "Weekly" (
set "FolderTime=%DateWeekly%"
) else (
set "FileNotMoved=1"
echo Undefined time for: %1
goto :EOF
)
mkdir "%CurrentFolder%\%NameTime%\%FolderTime%" 2>nul
move "%~1" "%CurrentFolder%\%NameTime%\%FolderTime%\" >nul
if errorlevel 1 (
set "FileNotMoved=1"
echo Failed to move file: %1
)
goto :EOF
The second batch code differs from first solution only on how subroutine MoveToFolder is coded for finding the corresponding folder for current file name. For that reason just the code of the subroutine is posted below.
:MoveToFolder
for /F "tokens=4,5 delims=-" %%A in ("%~1") do set "NamePart=%%A" & set "NameTime=%%B"
for /F "tokens=1* delims==" %%X in ('set FolderPart') do (
if not "!NamePart:%%Y=!" == "%NamePart%" (
set "FolderName=%%X"
goto FoundFolder
)
)
set "FileNotMoved=1"
echo Found no folder for: %1
goto :EOF
:FoundFolder
if /I "%NameTime%" == "Daily" (
set "FolderTime=%DateDaily%"
) else if /I "%NameTime%" == "Weekly" (
set "FolderTime=%DateWeekly%"
) else (
set "FileNotMoved=1"
echo Undefined time for: %1
goto :EOF
)
set "FolderIndex=%FolderName:~10%"
call set "CurrentFolder=%%FolderName%FolderIndex%%%"
mkdir "%CurrentFolder%\%NameTime%\%FolderTime%" 2>nul
move %1 "%CurrentFolder%\%NameTime%\%FolderTime%\" >nul
if errorlevel 1 (
set "FileNotMoved=1"
echo Failed to move file: %1
)
goto :EOF
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 /?
cd /?
echo /?
endlocal /?
for /?
goto /?
if /?
mkdir /?
move /?
pause /?
rem /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and >nul and answer on question Single line with multiple commands using Windows batch file for meaning of operator & on Windows command lines.
I have never done a batch file before. I have a few dozen .txt files sitting in a folder (ex. C:\files).
The files all end with 6 rows of text that need to be deleted. A sample would be (note spaces in first line):
var...
'ascending';...
'LIT-xxx,LIT-xxx...
setfunction...
0.33...
getdate...
Additionally, I would like the "new" files to overwrite the current files so that the file names and directory do not change.
abs 10.txt
him 4.txt
lab 18.txt
The following code snippet does exactly what you want, deleting the last six lines from text files:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "FILES=C:\files\*.txt" & rem // (specify file location and pattern)
set /A "LINES=-6" & rem /* (specify number of lines to delete;
rem positive number: delete from begin,
rem negative number: delete from end) */
rem // Standard `for` loop to resolve file pattern:
for %%F in ("%FILES%") do (
rem // Get the count of lines for the current file:
for /F %%N in ('^< "%%~F" find /C /V ""') do set "COUNT=%%N"
rem // Initialise a line index:
set /A "INDEX=-LINES"
rem /* Enumerate all lines of the current file, preserving empty ones
rem by preceding each with a line number, so no line appears empty
rem to the `for /F` loop; the line number is split off later on;
rem in addition, the current file is emptied after being read: */
for /F "delims=" %%L in ('
findstr /N /R "^" "%%~F" ^& ^> "%%~F" break
') do (
rem // Increment index, get text of currently iterated line:
set /A "INDEX+=1" & set "LINE=%%L"
rem // Toggle delayed expansion to preserve exclamation marks:
setlocal EnableDelayedExpansion
rem // Check index value and write to current file conditionally:
if !INDEX! GTR 0 if !INDEX! LEQ !COUNT! (
rem // Split off line number from line text:
>> "%%~F" echo(!LINE:*:=!
)
endlocal
)
)
endlocal
exit /B
This approach does not use temporary files in order to avoid any name conflicts. However, due to the fact that there are multiple file write operations for every single file, the overall performance is a bit worse than when writing all data to a temporary file at once and moving it back onto the original file.
Backup your original files to a different backup folder, then run this script:
#echo off
setlocal enabledelayedexpansion
pushd "%temp%\Test"
for %%G in ("*.txt") do (set "break="
(for /f "delims=|" %%H in (%%~G) do (
if not defined break (
echo %%H | findstr /r /b /c:"[ ]*var.*" >nul && set break=TRUE || echo %%H )
)) >> %%~nG_mod.txt
del %%~G & ren %%~nG_mod.txt %%G )
popd
exit /b
It assumed:
your 6 rows of text always start from [any number of spaces]var[any text] row, as you posted in the question, where only one string of such kind is present in any file
other 5 bottom rows don't need to match in every file
you save the files to filter in %temp%\Test, and there are no other unrelated files in that dir.
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&ersands. 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%