Batch file locating duplicate patterns in filename then process - windows

Ok, i've been working on a batch file for some time now, and im just stuck on the last bit.
What im trying to accomplish is to loop through a directory, create a variable which stores the filename of each file in the directory without the extension. Then for each file in the first loop, loop through a different directory and try to find any filename in the second loop that has the same name as stored in the variable, and then just output some simple text.
So for instance lets say in the first directory there is a filename called imafile-yehyeh.png, the variable will save imafile-yehyeh, then it will loop through all the files in the second directory, and output a message for each filename that has that pattern in it, so if a file in the second directory is called imafile-yehyeh_01.mp4 or imafile-yehyeh-newtitle.jpg, they would match the pattern and a message would output.
My script is looping and i am able to echo out all the variables, the files exist as i have created them exactly as shown above, but its not echoing out the filename is set for deletion line.
Any help would be greatly appreciated. my code is as follows;
#echo off
set "parent_folder=C:\Users\Testing\script"
set "dupe_folder=DUPEFOLDER"
set "kill_folder=1 SCANNED\thumb"
setlocal enableDelayedExpansion
for %%X in ("%parent_folder%\%dupe_folder%\*") do (
set dupe_pattern=%%~nX
for %%F in ("%parent_folder%\%kill_folder%\*") do (
echo %%~nF | FIND "%dupe_pattern%" 1>NUL && (
echo %%~F is set for deletion.
)
)
)
endlocal

Thanks to #Squashman the answer was to remove the set dupe_pattern.... line
and then change the FIND command to the following;
FIND "%%~nX"

Apart from needlessly setting a variable, as already pointed out, you are also making the script inefficient. For every file in the dupe_folder you are Echoing every file name in the kill_folder and piping that into a Find command looking for matches.
Here's a simpler way of doing it, (it matches file names which begin with the same string followed by a dot, as opposed to any file name containing the string anywhere).
#Echo Off
Set "parent_folder=C:\Users\Testing\script"
Set "dupe_folder=DUPEFOLDER"
Set "kill_folder=1 SCANNED\thumb"
CD /D "%parent_folder%" 2>Nul || Exit /B
For %%A In ("%kill_folder%\*") Do If Exist "%dupe_folder%\%%~nA.*" (
Echo %%A is set for deletion.)

Related

How to copy a file on a network drive to another folder on network and add time stamp to copied file?

This script looks at a network location for a folder name and specific file and then should copy the file to other folder on the network with current timestamp in destination file name.
Can you advise any syntax error or reason why it is not copying?
ECHO on
Title %0
set GETfn=Q:\Cdata\mm_tn
set GETfn=%GETfn: =%
echo GETfn = %GETfn%
set f1=%GETfn%%m%%d%%y%-%hr%%mn%.csv
set f1=%f1: =%
echo f1 = %f1%
copy GETfn.csv Q:\FTP\Sent\%f1%
dir Q:\FTP\Sent\
For example, if i have specificfile.csv on a mapped network drive Q:\Cdata\mm_tn\
then using this:
#echo off
:: src -> destination
set src=Q:\Cdata\mm_tn\
set dst=Q:\FTP\Sent\
echo -------------------------------------
echo source dir ---^> %src%
echo destination dir ---^> %dst%
echo -------------------------------------
:: timestamp
:: https://stackoverflow.com/questions/1064557/creating-a-file-name-as-a-timestamp-in-a-batch-job
FOR /F %%A IN ('WMIC OS GET LocalDateTime ^| FINDSTR \.') DO (
#SET B=%%A
)
set timestamp=%B:~4,2%_%B:~6,2%_%B:~0,4%_%B:~8,2%-%B:~10,2%
echo %timestamp%
:: copy from Q:\Cdata\mm_tn\ -> Q:\FTP\Sent\
copy %src%specificfile.csv %dst%%timestamp%_specificfile.csv
the specificfile.csv is copied to Q:\FTP\Sent\ as timestamped file 12_17_2020_12-39_specificfile.csv. Now paths can be easily adjusted for your requirements.
The second block is:
set GETfn=Q:\Cdata\mm_tn
set GETfn=%GETfn: =%
echo GETfn = %GETfn%
This block first defines the environment variable GETfn with string value Q:\Cdata\mm_tn. Next it uses a string substitution to remove all spaces from the string assigned to environment variable GETfn. That is of course a completely useless command line as the folder path assigned to the environment variable does not contain any space at all on having the batch file written without trailing space(s) on the line above. The last line of this block just outputs the fixed folder path which makes also no real sense.
The third block is:
set f1=%GETfn%%m%%d%%y%-%hr%%mn%.csv
set f1=%f1: =%
echo f1 = %f1%
It defines an environment variable f1 being a concatenation of the strings assigned to the environment variables GETfn (defined above), m, d, y, hr and mn not defined in posted code at all. So f1 is defined with Q:\Cdata\mm_tn-.csv which is of course not right. The next line is again useless as it removes all spaces from the string assigned to environment variable f1 not containing any space at all, except the batch file contains one or more trailing spaces at end of the line above. The third line just outputs the wrong defined environment variable f1.
I recommend to open a command prompt, run set /? and read the output help carefully and completely from top of first to bottom of last page. Next I suggest to read my answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? It has some useful additional information about the usage of command SET.
Please read carefully and completely my answer on Time is set incorrectly after midnight. Then you should have the knowledge why the following command line is a replacement for the entire posted batch file code.
#if exist "Q:\Cdata\mm_tn\GETfn.csv" for /F "tokens=1-5 delims=/: " %%I in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do #copy "Q:\Cdata\mm_tn\GETfn.csv" "Q:\FTP\Sent\GETfn_%%J%%K%%I-%%L%%M.csv" & goto :EOF
That command line copies the file Q:\Cdata\mm_tn\GETfn.csv to directory Q:\FTP\Sent with new file name GETfn_MMddyyyy-hhmm.csv using current date/time.
I would not use this date format for the destination file name. It would be better to use GETfn_yyyyMMdd-hhmm.csv, or better to read GETfn_yyyy-MM-dd_hh-mm.csv. This is the international date format which has the big advantage that files sorted by name are with such a date/time format sorted at the same time in chronological order. That is very often very helpful. So better in my opinion would be:
#if exist "Q:\Cdata\mm_tn\GETfn.csv" for /F "tokens=1-5 delims=/: " %%I in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do #copy "Q:\Cdata\mm_tn\GETfn.csv" "Q:\FTP\Sent\GETfn_%%I-%%J-%%K_%%L_%%M.csv" & goto :EOF
For understanding the used commands in the single command line and how they work, open a window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
copy /?
for /?
goto /?
if /?
robocopy /?
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file

Parsing every file in submap recursively to output folder whilst maintaining relativity

I'm using the following batch code to convert all files in a certain directory if the target file doesn't already exist however I'm stuck at getting this to run through every submap and file within that (and keep the output relative with that submap)
So I currently use this:
for %%f in (input/textures/*.*) do ( IF NOT EXIST "ouput/textures/%%~nf.dds" (
"bin/ThempImageParser.exe" "input/textures/%%f" "ouput/textures/%%~nf.dds"
)
)
This works perfectly for a single folder (as was intended), it takes all the files in that specific folder, and passes them as arguments to my executable, which then outputs the file on the path of the second argument.
However this also contains a flaw (this is an additional problem though..) as it does not work if the output -folder- does not exist, so if possible I'd also want it to create the folder if need be.
I've found some batch documentation (I really don't have much experience with Batch) showing me a command called FORFILES and the /R parameter, however I couldn't adjust this so it'd keep the relative paths for the output too, it'd require string manipulation and I have no clue on how to do that.
So the result I'm after is something like this, it takes any file deeper than "input/textures/ for example:
input/textures/some/very/deep/submap/why/does/it/go/on/myfile.anyExtension
it should then take that file (and relative path) and basically change "input" with "output" and replace the file extension with .dds like this:
ouput/textures/some/very/deep/submap/why/does/it/go/on/myfile.dds
and pass those two strings to my executable.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir\wherever\something"
FOR /f "delims=" %%a IN ('xcopy /y /L /s "%sourcedir%\*"') DO (
SET "destfile=%%a"
SET "destfile=!destfile:*%sourcedir%=%destdir%!"
IF /i "%%a" neq "!destfile!" (
FOR %%m IN ("!destfile!") DO IF NOT EXIST "%%~dpm%%~na.dds" (
ECHO MD "%%~dpm"
ECHO "bin\ThempImageParser.exe" "%%a" "%%~dpm%%~na.dds"
)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
First, perform an xcopy with the /L option to list-only the individual fullnames of files that would be copied by the xcopy.
Assign each name found from %%a to destfile, then remove all characters before the source-directoryname from that filename, and replace that string with the destination directoryname.
This will yield the destination name for the file (with the original extension). The only exception will be the very last output line, which is a count-of-files report. Since this line will not contain the source directoryname, the replacement will not take place, so %%a will be the same as !destfile! - so we eliminate that.
Now assign the destination filename to a metavariable so we can select its various parts, and if the filename made from the destination drive and pathname, the name part of the original file and .dds does not exist, then make the destination directoryname and execute the imageparser, providing the desired output filename.
Note that these last two are ECHOed instead of being executed for testing purposes. Remove the ECHOes to actually perform the command.
Note that / is a switch-indicator, \ is a directory-separator.
Note that MD will report an error if the directory already exists. Append 2>nul to the end of the md command to suppress that error message.

How to find particular contents in list of files

Please can someone tell how to find particular pattern in list of files.
I use the command:
dir *.txt /b /s >> C:\Users\Amrendra\Downloads\NIT_Testing\fileList.txt
In fileList.txt I have a list of files.
Now I want to find a certain pattern say abc in all the files listed.
So I want each line containing that pattern to be written into a new file.
Please suggest as I am completely new to batch command.
Thanks
I'm not sure if I got you completely right. You've echoed the names of all .txt files from some folder into one text file. Now you want to check it line by line for filenames containing some substring in the file name. Is that correct? If it is, here is the solution:
#ECHO OFF
SET targetFile=C:\Some\Path\SomeTextFile.txt
SET sourceFile=C:\Some\Path\Source.txt
SETLOCAL EnableDelayedExpansion
TYPE NUL>%targetFile%
FOR /F "tokens=*" %%L IN (sourceFile) DO (
SET templine=%%L
SET templine=!templine:abc=!
IF NOT !templine!==%%L (
ECHO %%L>>%targetFile%
)
)
Let's take a look on how it works: first we put the path to the file containing the file names into the variable %sourceFile% and the path to the target file into the variable %targetFile%. We need EnableDelayedExpansion to be able to work with changing variables inside the FOR-loop. TYPE NUL>%targetFile% simply clears the target file in case there are some entries from former runs.
The main work is done inside the FOR-loop. We read the source file line by line and want to check if the line contains the substring we are looking for. To do this we first store the line (%%L) in a temporary variable. Then we replace any occurrences of the search string with an empty string, means we simply remove abc from the temporary string. Finally we compare our modified temporary string with the original one. If they are equal we can be sure that the serch string abc was not in the original string. The other way around, if the strings differ, the substring was in the original string. So in the second case we do ECHO %%L>>%targetFile% which means we write the whole file name containing the search substring into our target file. Et voilĂ , we're done!
EDIT: As you want to search all listed files for the substring, here the new code:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET sourceFile=C:\Users\Amrendra\Downloads\NIT_Testing\fileList.txt
SET targetFile=C:\Users\Amrendra\Downloads\NIT_Testing\newList.txt
SET searchString=abc
TYPE NUL>%targetFile%
FOR /F "tokens=*" %%L IN (%sourceFile%) DO (
FINDSTR %searchString% "%%L">NUL
IF !ERRORLEVEL!==0 ECHO %%L>>%targetFile%
)
Here we are using findstr to check if a text file contains the string we are searching. As findstr usually would output the line which contains the searched string (which we don't want) we simply mute the command using >NUL. What we actually need is the ERRORLEVEL! If the string was found in a file findstr will set ERRORLEVEL to 0 or to 1 otherwise. So the only thing we have to do is to check if !ERRORLEVEL!==0.

String substition but whole line isn't copied

Hi I have this code to substitute the path and then run iexpress to build an exe.
#echo off
set PATH=PATH
set FOLDER=FOLDER
set NEWPATH=%~dp0
set TARGET=%NEWPATH%install_32bitWindows.EXE
echo %TARGET%
setlocal enabledelayedexpansion
for /f "tokens=2*" %%i in (install_32bitWindows.SED) do (
set str=%%i
echo %str%
set str=!str:%PATH%=%TARGET%!
set str=!str:%FOLDER%=%NEWPATH%!
echo !str! >> newfile
)
del install_32bitWindows.SED
rename newfile install_32bitWindows.SED
C:/Windows/SysWOW64/iexpress.exe /N %~dp0install_32bitWindows.SED
The substitution is done correctly but my problem is that one of the lines a space in it, and everything after the space isn't copied as such:
Originally
AppLaunched=c:\windows\system32\cscript.exe "install_32bitWindows.vbs"
After script:
AppLaunched=c:\windows\system32\cscript.exe
Why does the rest of the line get removed?
Thanks
Because all tokens after the second are concatenated into the implied %%j variable, which you are not referring to anywhere.
I 'm not sure what the correct processing would be, but this should point you to the correct direction:
>> newfile echo !str! %%j
I have moved the redirection to the beginning of the command so that the space before the >> is not spuriously inserted into the output file. Small stuff, but why not be precise?
In the absence of sample data, this becomes a bit of a guessing game.
Assuming your original ...SED filecontains lines of
string=anotherstring
and you wish to substitute different for certain fixed strings in that file, then
for /f "tokens=1*delims==" %%i in (install_32bitWindows.SED) do (
set str=%%j
(Note: 1* and %%j
should work BUT...
You appear to be substituting the newly-created file for the original, but the new file won't start string= because you don't output that part. If you want string=newstring then
echo %%i=!str! >> newfile
Finally, it's a really bad idea to use PATH as a user-variable in batch. PATH contains a semicolon-separated list of directories which batch uses to locate executables if the executable doesn't exist in the current directory. You change it, and batch will get amnesia vey quickly. Same goes for temp and tmp (point to a temporary directory) and a few other "reserved" names...

Loop over folder string and parse out last folder name

I need to grab the folder name of a currently executing batch file. I have been trying to loop over the current directory using the following syntax (which is wrong at present):
set mydir = %~p0
for /F "delims=\" %i IN (%mydir%) DO #echo %i
Couple of issues in that I cannot seem to pass the 'mydir' variable value in as the search string. It only seems to work if I pass in commands; I have the syntax wrong and cannot work out why.
My thinking was to loop over the folder string with a '\' delimiter but this is causing problems too. If I set a variable on each loop then the last value set will be the current folder name. For example, given the following path:
C:\Folder1\Folder2\Folder3\Archive.bat
I would expect to parse out the value 'Folder3'.
I need to parse that value out as its name will be part of another folder I am going to create further down in the batch file.
Many thanks if anyone can help. I may be barking up the wrong tree completely so any other approaches would be greatly received also.
After struggling with some of these suggestions, I found an successfully used the following 1 liner (in windows 2008)
for %%a in (!FullPath!) do set LastFolder=%%~nxa
You were pretty close to it :) This should work:
#echo OFF
set mydir="%~p0"
SET mydir=%mydir:\=;%
for /F "tokens=* delims=;" %%i IN (%mydir%) DO call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
#echo %LAST%
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER
For some reason the for command doesn't like '\' as a delimiter, so I converted all '\' to ';' first (SET mydir=%mydir:\=;%)
I found this old thread when I was looking to find the last segment of the current directory.
The previous writers answers lead me to the following:
FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
ECHO Last segment = "%_LAST_SEGMENT_%"
As previous have explained, don't forget to put quotes around any paths create with %_LAST_SEGMENT_% (just as I did with %CD% in my example).
Hope this helps someone...
This question's a little old, but I've looked for a solution more than once so here's a completely new take on it that I've just put together.
The trick is that we take the desired path, back up one level to create a folder mask for substitution and then replace the folder mask with nothing.
To test it, simple copy and paste into a command script (.cmd) in any directory, then run it. It will spit out only the deepest directory you're currently in.
Notes:
Replace %~dp0 with whatever path you like (as it is, it will return the deepest folder the batch file is run from. This is not the same as %cd%.)
When specifying the 'pathtofind' variable ensure there are no quotes e.g. c:\some path and not "c:\some path".
The original idea for folder masking is mine
Spaces in the path are no problem
Folder depth is not a problem
It was made possible by the genius of this batch scripting tip http://www.dostips.com/DtCodeBatchFiles.php#Batch.FindAndReplace
Hope this helps someone else.
#echo off
set pathtofind=%~dp0
if not exist %pathtofind% echo Path does not exist&pause>nul&goto :eof
cd /d %pathtofind%
set path1=%cd%
cd ..
set path2=%cd%
call set "path3=%%path1:%path2%\=%%"
echo %path3%
pause>nul
3 lines of script gets the result...
Found 2 additional ways to accomplish the goal, and unlike the other answers to this question, it requires no batch "functions", no delayed expansion, and also does not have the limitation that Tim Peel's answer has with directory deepness :
#echo off
SET CDIR=%~p0
SET CDIR=%CDIR:~1,-1%
SET CDIR=%CDIR:\=,%
SET CDIR=%CDIR: =#%
FOR %%a IN (%CDIR%) DO SET "CNAME=%%a"
ECHO Current directory path: %CDIR%
SET CNAME=%CNAME:#= %
ECHO Current directory name: %CNAME%
pause
REVISION: after my new revsion, here is an example output:
Current directory path: Documents#and#Settings,username,.sqldeveloper,tmp,my_folder,MY.again
Current directory name: MY.again
Press any key to continue . . .
This means that the script doesn't handle '#' or ',' in a folder name but can be adjusted to do so.
ADDENDUM: After asking someone in the dostips forum, found an even easier way to do it:
#echo off
SET "CDIR=%~dp0"
:: for loop requires removing trailing backslash from %~dp0 output
SET "CDIR=%CDIR:~0,-1%"
FOR %%i IN ("%CDIR%") DO SET "PARENTFOLDERNAME=%%~nxi"
ECHO Parent folder: %PARENTFOLDERNAME%
ECHO Full path: %~dp0
pause>nul
To return to the original poster's issue:
For example, given the following path:
C:\Folder1\Folder2\Folder3\Archive.bat
I would expect to parse out the value 'Folder3'.
The simple solution for that is:
for /D %%I in ("C:\Folder1\Folder2\Folder3\Archive.bat\..") do echo parentdir=%%~nxI
will give 'Folder3'. The file/path does not need to exist. Of course, .... for the parent's parent dir, or ...... for the one above that (and so on) work too.
Slight alteration for if any of the folders have spaces in their names - replace space to ':' before and after operation:
set mydir="%~p0"
set mydir=%mydir:\=;%
set mydir=%mydir: =:%
for /F "tokens=* delims=;" %%i IN (%mydir%) DO call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
set LAST=%LAST::= %
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER
Sheesh guys, what a mess. This is pretty easy, and it's faster to do this in memory without CD.
This gets the last two directories of a path. Modify it as required to get the last tokens of any line. My original code I based this on has more complexity for my own purposes.
Fyi, this probably doesn't allow paths with exclamation marks since I'm using enabledelayedexpansion, but that could be fixed.
It also won't work on a plain drive root. This could be averted in a number of ways. Check what the input path ends with, or a counter, or modifying the token and check behaviour, etc.
#echo off&setlocal enableextensions,enabledelayedexpansion
call :l_truncpath "C:\Windows\temp"
----------
:l_truncpath
set "_pathtail=%~1"
:l_truncpathloop
for /f "delims=\ tokens=1*" %%x in ("!_pathtail!") do (
if "%%y"=="" (
set "_result=!_path!\!_pathtail!"
echo:!_result!
exit/b
)
set "_path=%%x"
set "_pathtail=%%y"
)
goto l_truncpathloop
I modified answer given by #Jonathan, since it did not work for me in a batch file, but this below does work, and also supports folders with spaces in it.:
for %%a in ("%CD%") do set LastFolder=%%~nxa
echo %LastFolder%
This takes the current directory and echoes the last, deepest folder, as in below example, if the folder is this:
C:\Users\SuperPDX\OneDrive\Desktop Environment\
The batch code echoes this: Desktop Environment
In batch files in the FOR command you'll need to prepend %whatever with an extra % (e.g. %%whatever).
'echo %~p0' will print the currently directory of the batch file.
This is what we had in the end (little bit more crude and can only go so deep :)
#echo off
for /f "tokens=1-10 delims=\" %%A in ('echo %~p0') do (
if NOT .%%A==. set new=%%A
if NOT .%%B==. set new=%%B
if NOT .%%C==. set new=%%C
if NOT .%%D==. set new=%%D
if NOT .%%E==. set new=%%E
if NOT .%%F==. set new=%%F
if NOT .%%G==. set new=%%G
if NOT .%%H==. set new=%%H
if NOT .%%I==. set new=%%I
if NOT .%%J==. set new=%%J
)
#echo %new%
I don't know if it's the version of windows I'm on (win2k3), but the FOR loop isn't giving me anything useful for trying to iterate through a single string.
According to my observation (and the FOR /? info) you get one iteration for each line of input to FOR, and there is no way to change this to iterate within a line. You can break into multiple tokens for a given line, but it is only one invocation of the FOR loop body.
I do think the CALL :LABEL approach in these answers does a great job. Something I didn't know until looking at this was that ";" and "," are both recognized as argument separators. So once you replace backslashes with semicolons, you can call your label and iterate through with SHIFT.
So working off of what is posted by others here, I have the below solution. Instead of grabbing the last folder name, I actually wanted to find everything up until some known directory name.. this is what is implemented below.
#echo off
if "%1"=="" goto :USAGE
set FULLPATH=%~f1
set STOPDIR=%2
set PATHROOT=
:: Replace backslashes with semicolons
set FULLPATH=%FULLPATH:\=;%
:: Iterate through path (the semicolons cause each dir name to be a new argument)
call :LOOP %FULLPATH%
goto :EOF
:LOOP
::Exit loop if reached the end of the path, or the stop dir
if "%1"=="" (goto :EOF)
if "%1"=="%STOPDIR%" (goto :EOF)
::If this is the first segment of the path, set value directly. Else append.
if not defined PATHROOT (set PATHROOT=%1) else (set PATHROOT=%PATHROOT%\%1)
::shift the arguments - the next path segment becomes %i
SHIFT
goto :LOOP
:USAGE
echo Usage:
echo %~0 ^<full path to parse^> ^<dir name to stop at^>
echo E.g. for a command:
echo %~0 c:\root1\child1\child2 child2
echo The value of c:\root1\child1 would be assigned to env variable PATHROOT
Unfortunatelly, this is working great only when put on some depth but have problems with being on the very top of the mountain... Putting this program into "C:\Windows" e.g. will result with... "C:\Windows", not expected "Windows". Still great job, and still damage can be repaired. My approach:
#echo off
set pathtofind=%~dp0
if not exist %pathtofind% echo Path does not exist&pause>nul&goto :eof
cd /d %pathtofind%
set path1=%cd%
cd ..
set path2=%cd%
set path4=%~dp1
call set "path3=%%path1:%path2%\=%%"
call set "path5=%%path3:%path4%*\=%%"
echo %path5%
pause>nul
And it's working just fine for me now, thanks for the idea, I was looking for something like that for some time.

Resources