How to Delete All files But Excluding Specific Files (Batch File) - command-line

I know there are questions here like this before but in my case there are special conditions in specifying the files to be excluded in the "delete" operation.
Filename examples:
A_001.xml
A_002.xml
B_001.xml
B_002.xml
B_003.xml
C_009.xml
D_002.xml
The files above are in one directory and I need to delete all files but retain one copy of each file with the highest counter in filename which are A_002.xm, B_003.xml, C_009.xml and D_002.xml.
Is this possible? I'm new in batch file creation so please help me.. I already know how to delete files and even exclude a specific file extension but I don't know what to do in this scenario.

#ECHO OFF
SETLOCAL enabledelayedexpansion
::
:: establish target directory
:: initialise PREFIX to a name that's invalid in a filename
::
SET target=.
SET prefix=:
FOR /f "tokens=1*delims=_" %%i IN (
'dir /b/a-d/o-n "%target%\*_*.xml'
) DO (
IF !prefix!==%%i ECHO DEL "%target%\%%i_%%j"
SET prefix=%%i
)
This solution relies on the presence of "_" in the names of the *xml files in the target directory. I tested it on some dummy files in my batch-development directory, hence I used . for the current directory
The DIR command produces a list of the files in the target directory that match the pattern *_*.xml The/b switch means filename only, /a-d means no directory names, even if they match and /o-n means in the reverse order of name Therefore, the names will appear with the highest numeric value of the part after _ first.
The FOR reads this output and assigns the prefix to %%i and the remainder of the filename to %%j Since prefix is initialised to an invalid-in-filenames character, it cannot possibly match %%i for the first file encountered, so the DEL will not be executed. The last-encountered prefix will then be stored. Only if the next filename has a matching prefix will the DEL be executed
Note that I've inserted the ECHO keyword before the DEL. This is a safety device in case of error. The PROPOSED DEL operation will be reported to the screen, not EXECUTED. Once you're satisfied that the batch is correct, remove the ECHO and the delete operation will proceed.
Point to be observed:
Some editors are quite cavalier about stripping or retaining trailing
spaces. Normally, this is harmless but trailing spaces on a SET
command in batch can cause chaos.

#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set newprefix=_
for /F %%a in ('dir /on /b /a-d *.xml') do (
for /F "delims=_" %%i in ("%%a") do (
IF NOT *%%i*==*!newprefix!* (
for /F %%q in ('dir /on /b /a-d %%i*.xml') do set lastitem=%%q
attrib +r !lastitem! > NUL
del /s /q %%i*.xml 1>NUL 2>NUL
attrib -r !lastitem! > NUL
set newprefix=%%i
)
)
)
For each prefix of xml file, lists all the files with the prefix in question to get the last one in the list. Then mark the last one read only. Delete all with given pattern except read only. Reset read only flag and do over for next prefix.

Related

How to create directory from URL path separator (forward slash) when that is present in FileName coded %2f or %2F

As I mentioned in Q-title, I have setup a sublime settings auto-syncing process to Github Gist of my choice when I have passed correct Access Token.
Now this process syncs any file that is present inside a folder(s), considering the relative paths, as DirName%2FFileName.extension where DirName and/or FileName may contain singular spaces(%20) between words(if >2 words in either).
Now when I download that gist thro' DownloadZIP button, it ofcourse downloads the whole gist with multiple files as single zip, which after download can be extracted into it's folder in local system(Windows 10/11 OS).
So, the question finally: When I have downloaded such zip and extracted into folder that contains one/multiple files that have %2f or %2F in their names, how do I create a directory from those filenames and move those files into those directory thro' a batch file if possible, or a PowerShell command, but runnable from Batch script, and then also rename those files to remove all DirName%2f parts from their names ? I know about mv and md commands but how do I create directory from URL coded character group and do those operations ?
Note: DirName%2f parts could be occurring in multiples like DirName1%2fDirName2%2f... if the file that is being synced from local is inside mainfolder/subfolders... relatively.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "#name1=DirName%%2f"
SET "#name2=DirName1%%2fDirName2%%2f"
SET "#name3=DirName1%%2FDirName2%%2F"
SET "#2f=%%2f"
SET "#subs=ABC"
SET #
FOR /f "delims=" %%e IN ("%#2f%") DO (
FOR /f "delims=" %%w IN ("!#name1:%#2f%=%#subs%!") DO ECHO REN "%#name1%" "%%w"
FOR /f "delims=" %%w IN ("!#name2:%#2f%=%#subs%!") DO ECHO REN "%#name2%" "%%w"
FOR /f "delims=" %%w IN ("!#name3:%#2f%=%#subs%!") DO ECHO REN "%#name3%" "%%w"
)
ECHO ===================
FOR %%b IN ("%#name1%" "%#name2%" "%#name3%") DO (
SET "#name=%%~b"
FOR /f "delims=" %%e IN ("%#2f%") DO (
FOR /f "delims=" %%w IN ("!#name:%#2f%=%#subs%!") DO ECHO REN %%b "%%w"
)
)
GOTO :EOF
This demo shows how in batch.
You can try :
[System.Web.HttpUtility]::UrlDecode("DirName%2FFileName.extension")
it gives :
DirName/FileName.extension
Then you can use the CmdLets with noun "Path" to manage your file names.
Get-Command -Noun path
If it exists some / in place of \, Powershel should manage them.
The to work with files and folders is given with CmdLet with noun Item :
Get-Command -Noun item
For example to create a directory :
New-Item -name toto -ItemType Directory

How to use "ren" or "rename" to truncate the file name until underscored?

I have some file names as below saved in C:\aaa\temp
Before:
92485345_A0027777882244.zip
87493354_A0027684085444.zip
87111901_A0027871905777.zip
some fixed rule in the file name:
I need to delete the characters until the underscore "_"
The name after underscore always begins with A0027
Can someone please teach me how to write a script to batch rename them as below:
After:
A0027777882244.zip
A0027684085444.zip
A0027871905777.zip
If the number of characters in front of the underscore _ is always the same, then use this:
rem // Expecting 8 characters in front of `_`:
ren "????????_A0027*.zip" "/////////*.*"
This is an undocumented feature of ren, which is described there.
When the number of characters in front of _ can vary you have to use dir /B to get the list of matching files, a for /F loop to capture it and to split the file names, then ren to finally rename each file:
for /F "tokens=1* delims=_" %E in ('dir /B /A:-D "*_A0027*.zip"') do #ren "%E_%F" "%F"
To use this code in a batch-file you need to change % to %%:
for /F "tokens=1* delims=_" %%E in ('dir /B /A:-D "*_A0027*.zip"') do (
ren "%%E_%%F" "%%F"
)
List the files from the directory.
Iterate through the list of names.
Remove chars up until the underscore (including).
Rename each file to the new name as you iterate through list.
Now, if you chose to do this with a script or pipe through, it is up to you.

Copy folder structure, but latest file only

I have many numbered directories, each containin a set of files:
Folder_abc
index.xml
000001.doc
000002.doc
000003.doc
000004.doc
Folder_bdf
index.xml
000001.xls
000002.xls
Folder_...
DESIRED RESULT (latest version only)
Folder_abc
index.xml
000004.doc
Folder_bdf
index.xml
000002.xls
Folder_...
There is an index.xml in each directory, this of course could be copied with robocopy, seperately if necessary.
I am looking for a solution in powershell or batch-file, perfect would be to use robocopy, though I don't see an approach.
This is basically the code from my comment as an answer with some explanatory remarks and a minor correction, namely excluding index.xml from the files processed by the inner loop:
#echo off
rem // Define constants here:
set "ROOT=D:\Path\To\Source"
set "DEST=D:\Path\To\Destination"
set "NAME=Folder_*" & rem // (name/mask of source sub-directories)
set "FILE=index.xml" & rem // (name of file that must always be copied)
rem // Loop over all sub-directories in the root source directory:
for /D %%D in ("%ROOT%\%NAME%") do (
rem // Reset variable that will hold name of file to copy later:
set "LAST="
rem /* Get all files in sorted manner, except the one that must always be copied;
rem store the name in a variable, which will hold the last file name finally;
rem `/O:D` regards the last modification date; add `/T:C` for the creation date;
rem to sort by name rather than by date, replace `/O:D` by `/O:N` or `/O:NE`: */
for /F "delims= eol=|" %%F in ('
2^> nul dir /B /A:-D /O:D "%%~D\*.*" ^| findstr /I /V /C:"%FILE%"
') do set "LAST=%%F"
rem /* Create destination sub-directory with the same name as the source sub-directory;
rem potential error message in case the sub-directory already exists are suppressed: */
2> nul md "%DEST%\%%~nxD"
rem // Copy the file that always needs to be copied, if there is such:
if exist "%%~D\%FILE%" copy /-Y "%%~D\%FILE%" "%DEST%\%%~nxD\"
rem // Copy the previously determined last file, if any has been found:
if defined LAST call copy /-Y "%%~D\%%LAST%%" "%DEST%\%%~nxD\"
)
I am assuming that the most recenr file couldbe an arbitrarynumber of days old? Is that true?
If all of your directories are only 1 layer deep on a single root folder, then:
#(
SetLocal EnableDelayedExpansion
Echo Off
)
SET "_Root=C:\Some\Folder\Path"
SET "_NewRoot=C:\Some\New\Folder"
For /D %%A In ("_Root") Do (
MD "%_NewRoot%\%%A"
Copy "%_Root%\%%A\index.xml" "%_NewRoot%\%%A\index.xml"
For /F "Tokens=*" %%a IN ('DIR /A-D /B /OD "_Root\%%A\" ^| FindStr /I /V /C:"index\.xml$"') Do (
SET "_File=%%a"
)
Copy "%_Root%\%%A\!_File!" "%_NewRoot%\%%A\!_File!"
)
So, essentially, we need to loop the directory structure and set the value od the _File variable to the files found and, if we order them by date in ascending order, the last value found will he the newest folder, so when we kick out of that loop the variable has been populated with the value you need.
since index is always needed I create the folders and copy index right away and do the second file copy only after a match is found we do ignore "Index.xml" as its already copied and not an appropriate newest file to match anyway

Trying to rename files based on file size

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_:

Batch file : copy all file except those its name contain some substring

first of all im beginner. i want to create batch file to search through specific folder (including all it subfolder) and copy all file inside it except those which filename contain some specific string,this is what i have so far
set now=fish
set logDirectory="C:\Users\paiseha\Desktop\bb\"
for /r %logDirectory% %%i IN (*%now%*.*) do (
rem copy process goes here
)
let say i have 3 file in it
C:\Users\fareast\Desktop\bb\one.txt
C:\Users\fareast\Desktop\bb\twofishtwo.txt
C:\Users\fareast\Desktop\bb\three.txt
so i want to copy file one.txt and three.txt only, but instead it copy only the second one,i know its because of *%now%*.* so how can i invert it so that it does the other way around, help me pls, thanks in advance
try:
#ECHO OFF &setlocal
set "now=fish"
set "logDirectory=C:\Users\paiseha\Desktop\bb"
for /f "delims=" %%a in ('dir /a-d/b/s "%logDirectory%"^|findstr /riv "^.*[\\][^\\]*%now%[^\\]*$"') do (
rem copy process goes here
)
EDIT: The \ character is represented as [\\] instead of \\ because of a quirk on how Vista FINDSTR regex escapes \. Vista requires \\\\, but XP and Win 7 use \\. The only representation that works on all platforms is [\\]. See What are the undocumented features and limitations of the Windows FINDSTR command? for more info.
for /f "delims=" %%a in ('dir /a-d/s/b "%logDirectory%" ') do echo %%~nxa|findstr /i /L "%now%" >nul&if errorlevel 1 ECHO COPY "%%a"
should work for you.