I have up to 4 files based on this structure (note the prefixes are dates)
0830filename.txt
0907filename.txt
0914filename.txt
0921filename.txt
I want to open the the most recent one (0921filename.txt). how can i do this in a batch file?
Thanks.
This method uses the actual file modification date, to figure out which one is the latest file:
#echo off
for /F %%i in ('dir /B /O:-D *.txt') do (
call :open "%%i"
exit /B 0
)
:open
start "dummy" "%~1"
exit /B 0
This method, however, chooses the last file in alphabetic order (or the first one, in reverse-alphabetic order), so if the filenames are consistent - it will work:
#echo off
for /F %%i in ('dir /B *.txt^|sort /R') do (
call :open "%%i"
exit /B 0
)
:open
start "dummy" "%~1"
exit /B 0
You actually have to choose which method is better for you.
Sorry, for spamming this question, but I just really feel like posting The Real Answer.
If you want your BATCH script to parse and compare the dates in filenames, then you can use something like this:
#echo off
rem Enter the ending of the filenames.
rem Basically, you must specify everything that comes after the date.
set fn_end=filename.txt
rem Do not touch anything bellow this line.
set max_month=00
set max_day=00
for /F %%i in ('dir /B *%fn_end%') do call :check "%%i"
call :open %max_month% %max_day%
exit /B 0
:check
set name=%~1
set date=%name:~0,4%
set month=%date:~0,2%
set day=%date:~2,2%
if /I %month% GTR %max_month% (
set max_month=%month%
set max_day=%day%
) else if /I %month% EQU %max_month% (
set max_month=%month%
if /I %day% GTR %max_day% (
set max_day=%day%
)
)
exit /B 0
:open
set date=%~1
set month=%~2
set name=%date%%month%%fn_end%
start "dummy" "%name%"
exit /B 0
One liner, using EXIT trick:
FOR /F %%I IN ('DIR *.TXT /B /O:-D') DO NOTEPAD %%I & EXIT
EDIT:
#pam: you're right, I was assuming that the files were in date order, but you can change the command to:
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT
then you have the file list sorted by name in reverse order.
Here you go... (hope no-one beat me to it...) (You'll need to save the file as lasttext.bat or something)
This will open up / run the oldest .txt file
dir *.txt /b /od > systext.bak
FOR /F %%i in (systext.bak) do set sysRunCommand=%%i
call %sysRunCommand%
del systext.bak /Y
Probably XP only. BEHOLD The mighty power of DOS.
Although this takes the latest filename by date - NOT by filename..
If you want to get the latest filename, change /od to /on .
If you want to sort on something else, add a "sort" command to the second line.
Use regular expression to parse the relevant integer out and compare them.
Related
I am trying to rename some MP4 files based on file size of mp4 files in another directory. I want to name all files with identical sizes to same name. Meaning if the file size of the source file matches the size of file in the comparison directory, the source file is renamed to whatever the compared file is named. Because both directories need to be read recursively I'm thinking it would be easier to make a list for comparison with the info in it in 2 columns by using the DIR /s /b echo %%~zs>>filesizelist.txt command giving me a list like
123456789 movie.mp4
987654321 movie2.mp4
Then pass all source mp4s to the batch file and if %%~za matches a value in first column then ren the file to the
corresponding filename. Is this the best path? I tried to script it to work on the fly and that was both a no-go and the source of my 3 day headache(plus the reference list rarely changes and is obviously easily updated). Can someone please assist me with the script?
I do some test with my mp4, and the code works, and for you perform your test, you w´ll need change/put this 2 line above with the path to your folder/directory (one to keep and another to compare), by replacing in the line code is like this:
`set "_target_to_keeped=C:\Users\ecker\Videos\Target"`
`set "_target_to_rename=C:\Users\ecker\Videos\Ren_it"`
You need add the folder where are files to keep and files to rename (if size+name match) on same lines where are the 2 lines code up in this test (sorry not explain in good English, my English is not help me). By now, is late 01:53, i need sleep... yep! so, have nice code!
#echo off && setlocal enableextensions enabledelayedexpansion
cd /d "%~dp0"
set /a _cnt_in_looping= 1 - 1
set /a _cnt_files_size= 1 - 1
set "_target_to_keeped=C:\Users\ecker\Videos\Target"
set "_target_to_rename=C:\Users\ecker\Videos\Ren_it"
cd /d "!_target_to_keeped!"
for /f "tokens=* delims=^ " %%i in ('^<nul dir /o-d /on /b "*.mp4" 2^> nul ^| find "" /v /c') do set _cnt_in_looping=%%i
for /f "tokens=* delims=^ " %%i in ('^<nul dir /o-d /on /b "*.mp4"') do (
set "_file_now_keep=%%i"
set "_file_now_keeped=%%~zi %%i"
call :_to_compare_:
)
set /a _total_files_renamed=!_cnt_in_looping! - !_cnt_files_size!
set /a _total_files_n_chang=!_total_files_renamed! - !_cnt_in_looping! * -1
echo/Total of files renamed = !_total_files_renamed!
echo/Total of files n chang = !_total_files_n_chang!
endlocal
goto :_end_of_file_:
:_to_compare_:
if not exist "!_file_now_keep!" exit /b
for /f "tokens=*" %%I in ('^<nul dir /o-d /on /b "!_file_now_keep!"') do (
set "_file_now_compare=%%~zI %%I"
set "_path_now_compare=%%~dpI"
if "!_file_now_compare!" == "!_file_now_keeped!" (
rename "!_path_now_compare!\%%I" "%%~zI %%I"
echo/ rename "%%~I" "%%~zI %%I"
if ["!errorlevel!"]==["0"] call set /a _cnt_files_size=!_cnt_files_size! + 1
timeout /t 10
)
)
exit /b
:_end_of_file_:
How do I make a workaround for the FOR Loop? The problem is that DelayedExpansion fails for filenames with "!" in them
The code (this is part of a bigger script)
:FileScan
::Sets the filenames(in alphabetical order) to variables
SETLOCAL EnableDelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
::Sets the foldernames(in alphabetical order) to variables
SET "FolderCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n /a:d "%~dp0Put_Your_Files_Here" 2^>nul') DO (
SET /a "FolderCount+=1"
SET Folder!FolderCount!=%%x
)
If not possible in batch how do I do it in PowerShell
to something that can be called by the original batch like:
:FileScan
Call %~dp0FileScanScript.ps1
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
Call %~dp0FolderScanScript.ps1
EDIT: CALL SET fixed the original code avoiding DelayedExpansion altogether
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
CALL SET "Ep%%EpCount%%=%%x"
)
The easiest solution is indeed to use callset to introduce another parsing phase:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%x"
)
popd
endlocal
However, this causes problems as soon as carets ^ appear in the strings or file names, because call doubles them when they appear quoted.
To solve this, you need to make sure that the strings are expanded in the second parsing phase too, which can be achieved by using an interim variable, like this:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set "Episode=%%x"
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%Episode%%"
)
popd
set Ep
endlocal
In addition, I added the filter option /A:-D to dir to ensure that no directories are returned. Furthermore, I used pushd and popd to change to the parent directory of the script temporarily. The dir command line as you wrote it, searched files *.mkv* in the parent directory of the script, but all the other ones in the current working directory, which is probably not what you wanted.
Another option is to toggle delayed expansion, so that the for variable reference %%x becomes expanded when delayed expansion is disabled, and the assignment to the Ep array-style variables is done when it is enabled. But you need to implement measures to transport variable values beyond the endlocal barrier then, like in the following example:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
rem /* Delayed expansion is disabled at this point, so expanding
rem the `for` variable reference `%%x` is safe here: */
set "Episode=%%x"
set /A "EpCount+=1"
rem // Enable delayed expansion here:
setlocal EnableDelayedExpansion
rem /* Use a `for /F` loop that iterates once only over the assignment string,
rem which cannot be empty, so the loop always iterates; the expanded value
rem is stored in `%%z`, which must be assigned after `endlocal` in order
rem to have it available afterwards and not to lose exclamation marks: */
for /F "delims=" %%z in ("Ep!EpCount!=!Episode!") do (
endlocal
set "%%z"
)
)
popd
set "Ep"
endlocal
CALL SET Ep%%EpCount%%=%%x
would set your variables appropriately, as would
FOR /f "tokens=1*delims=[]" %%x IN (
'Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul ^|find /v /n ""'
) DO (
SET /a epcount=%%x
CALL SET ep%%x=%%y
)
(prefix each name with [num] then use for to extract num to %%x and name to %%y (assuming there are no names that start [ or ]))
Note: The OP had originally tagged the question powershell in addition to batch-file, but later removed that tag (since restored).
The equivalent of the following batch-file (cmd) loop:
SETLOCAL EnableDelayedExpansion
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
in PowerShell (PSv3+) is:
# Collect all episode paths in array $Eps
$Eps = (Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv, *.mp4, *.avi, *.flv).FullName
# Count the number of paths collected.
$EpCount = $Eps.Count
The above will work with paths that have embedded ! instances too.
$Eps will be an array of the full filenames.
To then process the matching files one by one:
foreach ($ep in $Eps) { # Loop over all files (episodes)
# Work with $ep
}
Using a single array rather than distinct $Ep1, $Ep2, ... variables makes for much simpler and more efficient processing.
The above is the fastest way to process the already-collected-in-memory filenames one by one.
A more memory-efficient (though slightly slower), pipeline-based approach would be:
Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv,*.mp4,*.avi,*.flv | ForEach-Object {
# Work with $_.FullName - automatic variable $_ represents the input object at hand.
}
I have a Script that is searching for the newest file in the directory "test" and gives it the variable "Newest".
pushd c:\Test
for /f %%i in ('dir /b/a-d/od/t:c') do set Newest=%%I
Now let's assume that the name of the file is ThisFile.txt.
I now want to rename it to ThisFile_yyyymmdd.txt by using my variable %Newest%.
What I have so far is this:
Set TDate=%date:~6,6%%date:~3,2%%date:~0,2%
ren %Newest% "%Newest%%TDATE%"
This however renames my file to ThisFile.txt_yyyymmdd which obviously removes the extension and ruins the file.
Does anyone have a solution for this?
Keep in mind that I have to rename it by using the variable %Newest%.
Here you go . Also %%i are case sensitive.
#echo off
setlocal
pushd c:\Test
Set "TDate=%date:~6,6%%date:~3,2%%date:~0,2%"
for /f %%I in ('dir /b/a-d/od/t:c') do (
echo ren %%~fI "%%~nI_%TDATE%%%~xI"
)
exit /b 0
Must read FOR/?
I need to do a batch file which order all files by a date interval, example:
#echo off
echo Input the date(dd/mm/yyyy):
set /p compDate=
::After that I will compare from the actual day (%date%), example:
set interval = %compDate% - %date%... *Something like that*
::After that I need to list all files from a specific directory, example:
echo Input the directory:
set /p directory=
SET Exit= %UserProfile%\Desktop\test.txt
::After that I might need dir /tc to get the creation date, example:
pushd "%directory%"
dir /s /tc /a-d > %Exit%
::After that I don't know how to get only the lines which are in date interval, example:
Today is 19/08/2014, but I want to search all files created from day 10/07/2014.
So I have to copy all lines which have the date 10/07/2014, 11/07/2014, 12/07/2014 and so on until stop on today created files.
I tried with findstr, but I can't set the date interval, just a specific date to search in the .txt created.
Somebody know how to do that?
If I correctly understood the request, you really don't want files created in a given interval, but files created after a given date. The Batch file below assume that the date used by the system appear in DD/MM/YYYY order:
EDIT: Some modifications as reply to the comments
#echo off
setlocal EnableDelayedExpansion
echo Input the date(dd/mm/yyyy):
set /p compDate=
for /F "tokens=1-3 delims=/" %%a in ("%compDate%") do set compDate=%%c%%b%%a
echo Input the directory:
set /p directory=
SET Exit=%UserProfile%\Desktop\test.txt
pushd "%directory%"
(for /F "tokens=1-5*" %%a in ('dir /s /od /tc /a-d') do (
set "fileDate=%%a"
if "!fileDate:~2,1!!fileDate:~5,1!" equ "//" (
for /F "tokens=1-3 delims=/" %%x in ("!fileDate!") do set fileDate=%%z%%y%%x
if !fileDate! geq %compDate% (
set "fileSize= %%e"
echo %%a %%b %%c %%d !fileSize:~-16! %%f
)
)
)) > %Exit%
popd
A solution that uses WMIC and is independent from time/date settings:
#echo off
setlocal
set /p compDate=Input the date(yyyymmdd):
set /p directory=Full directory path (with no slash at the end):
set exit_file= %UserProfile%\Desktop\test.txt
break>%exit_file%
for /f "tokens=1,2 delims=:" %%a in ("%directory%") do (
set "dir_drive=%%~a:"
set "dir_path=%%~b\"
)
set dir_path=%dir_path:\=\\%
setlocal enableDelayedExpansion
for /f "useback skip=1 tokens=1,2* delims=:" %%f in (`" wmic datafile where (drive='!dir_drive!' and path like '%dir_path%') get CreationDate^,name"`) do (
set creation_date=%%f
set creation_date=!creation_date:~0,8!
set "file_name=%dir_drive%%%~g"
if 1!creation_date! GTR 1%compDate% (
echo !file_name!>>%exit_file%
)
)
exit /b 0
I need to scan through a drive and list all the folders containing only a single file, entitled "Thumbs v0.1.db". I have cobbled together the following code but it doesn't seem to work. Either the batch file exits prematurely, or it completes without listing any such subdirectories! I would be very thankful if someone could point out the problem.
#echo off
SET /P folder="Please enter root directory to seach in: "
SET writefile="C:\Users\MYNAME\Desktop\Thumbs.txt"
SET tmp="C:\Users\MYNAME\Desktop\rowcounttmp"
Echo Searching for directories, please wait...
echo Thumbs v0.1.db-only directories in %folder% > %writefile%
cd /D %folder%
for /d /r %1 %%A in (.) do (
dir /a /b "%%~fA" 2>nul | find /c /v "~StringWhichWillNotAppear~" > %tmp%
set var=<%tmp%
if [%var%] == 1 dir /a /b "%%~fA" 2>nul | findstr /i "Thumbs v0.1.db" >nul && echo %%~fA >> %writefile%
)
del %tmp%
Pause
Thanks
EDIT:
Thanks to #peter-wright, I now have this: Have I implemented his suggestions incorrectly? It seems to crash for certain filepaths (specifically the read-only drive I am trying to scan through) and there are still no results appearing.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
Color 0A
SET /P folder="Please enter root directory to seach in: "
SET writefile="C:\Users\MYNAME\Desktop\Thumbs.txt"
SET tempfile="C:\Users\MYNAME\Desktop\rowcounttmp"
Echo Searching for directories, please wait...
echo Thumbs v0.1.db only directories in %folder% > %writefile%
cd /D %folder%
for /d /r %1 %%A in (.) do (
dir /a /b "%%~fA" 2>nul | find /c /v "~StringWhichWillNotAppear~" > %tempfile%
set var=<%tempfile%
if !var! == 1 dir /a /b "%%~fA" 2>nul | findstr /i "Thumbs v0.1.db" >nul && echo %%~fA >> %writefile%
)
del %tempfile%
Pause
The variable var is being set within a BLOCK (parenthesised series of statements.)
Any %var% within a block is replaced by the value of that var WHEN THE BLOCK IS PARSED, not when it is EXECUTED.
To access the RUN-TIME value of var use !var! AFTER having invoked delayedexpansion with a SETLOCAL ENABLEDELAYEDEXPANSION statement (probably best implemented immediately after your #echo on.
Danger, Will Robinson : DO not use tmp as a variable name - tmp is a magic variable with special meaning to batch. Others may be listed by invoking
SET
from the prompt. Also avoid DATE, TIME, RANDOM, CD and a few others. And really not a good idea to use executable names or batch keywords either...
tip:
try using
FOR /F "delims=" %%i in ('dir /s /a /b /a-d "Thumbs v0.1.db" ') do (
and then %%~dpi will be assigned the names of the directories that CONTAIN the target file. If the count-of-files in these directories is not ==1 then it is not alone...