BAT file - replace multiple characters in filename - windows

Couldn't find an answer that wasn't very specific to someone else's problem.
I'd like to place a bat file in a directory and run it to achieve the following:
Replace all initial '-' (hyphen) with ' - ' (space-hyphen-space)
Replace any 3 char Month names (Jan,Feb,...Dec) with two-digit month number preceeded and followed by a hyphen ('Jan' = '-01-' , 'Mar' = '-03-')
So the following:
32432492-2015Jan23-2015Feb23.pdf
32432492-2015Feb24-2015Mar24.pdf
32432492-2015Mar25-2015Apr29.pdf
becomes:
32432492 - 2015-01-23 - 2015-02-23.pdf
32432492 - 2015-02-24 - 2015-03-24.pdf
32432492 - 2015-03-25 - 2015-04-29.pdf
I'd like the "rename" to only run once (instead of renaming all files over and over). It should do this for all files in current directory (except the current bat file of course).

#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*-*" '
) DO (
SET "newname=%%a"
FOR %%b IN ("Jan=-01-" "Feb=-02-" "Mar=-03-" ) DO SET "newname=!newname:%%~b!"
FOR /f "tokens=1*delims=-" %%b IN ("!newname!") DO SET "newname=%%b - %%c"
IF /i NOT "!newname!"=="%%a" ECHO(REN "%sourcedir%\%%a" "!newname!"
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I expect that you would have the sense to complete the month/number set in the form given.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
Revision
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*-*" '
) DO (
SET "newname=%%a"
FOR %%b IN ("Jan=/01/" "Feb=/02/" "Mar=/03/" ) DO SET "newname=!newname:%%~b!"
FOR /f "tokens=1,2*delims=-" %%b IN ("!newname!") DO SET "newname=%%b - %%c - %%c"
IF /i NOT "!newname!"=="%%a" ECHO REN "%sourcedir%\%%a" "!newname:/=-!"
)
GOTO :EOF
You said "Replace all initial '-' (hyphen) with ' - ' (space-hyphen-space)" which I took to mean "the initial hyphen in the name".

Related

File/Folder sorting by semi-complex names

I need to sort a series of images into folders by part of their names.
The only thing consistent is a series of numbers after an underscore.
I would like to use the content before the underscore as the folder name.
The problem is that some of the images have an underscore in the name.
I'm just not getting the variables right...
Currently using:
#echo off
setlocal enabledelayedexpansion
for %%A in (*.jpg) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "tokens=1* delims=_" %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! does not exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
Examples of problematic names:
a_t_r_a_v__a_83563614_2994781403891160_6736610473828214002_n.jpg
alexa_vn10s1ty429_81834750_1570208686488587_1442450484794514255_n.jp
More common names:
shurra.m.lance_96141088_342661823370490_6342263806980952485_n.jpg
anu.m0111_104134441_573701159868969_589828829350351913_n.jpg
Currently hoping to get these turned into folders as:
a_t_r_a_v__a
alexa_vn10s1ty429
shurra.m.lance
anu.m0111
Just not getting it to ignore the right underscores.
Thanks for your time in advance. I am learning so much here but I guess I'm just missing the last piece.
This is not a trivial task, but I think I have found a solution (see all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* Loop through matching files; `findstr` additionally filters them, so only
rem items containing an underscore followed by a decimal digit are processed;
rem exclamation marks are prohibited in the portion behind the prefix: */
for /F "delims= eol=|" %%F in ('
dir /B /A:-D-H-S "*_*.jpg" ^| findstr /RIC:"[^_]_[0123456789][^!]*\.jpg$"
') do (
rem // Store current file name:
set "FILE=%%F" & set "TEST=%%F"
rem // Toggle delayed expansion to avoid problems with exclamation marks:
setlocal EnableDelayedExpansion
rem // Define ascending and descending number sequences for the next loop:
for %%J in ("0,1,9" "9,-1,0") do (
rem // Loop through all decimal digits, increasing, then decreasing:
for /L %%I in (%%~J) do (
rem // Find `_` plus the current digit, remove everything in front:
if not "!TEST:*_%%I=!"=="!TEST!" set "NAME=!TEST:*_%%I=_%%I!"
)
rem /* Remove previously end of file name to get the desired prefix
rem (at this point exclamation marks could cause problems): */
for /F "delims= eol=|" %%E in ("!NAME!") do set "TEST=!TEST:%%E=!"
)
rem // Create desired directory and move current file into it:
ECHO md "!TEST!" 2> nul
ECHO move /Y "!FILE!" "!TEST!\"
endlocal
)
endlocal
exit /B
After having tested for the correct output, remove the upper-case ECHO commands.
This is a simpler (and faster) method:
#echo off
setlocal EnableDelayedExpansion
set "digits=0123456789"
for %%A in (*.jpg) do (
set "fname=%%A"
rem Initialize "first digit after an underscore"
set "firstDigit="
rem Split name at underscores
for %%a in ("!fname:_= " "!") do (
if not defined firstDigit (
set "part=%%~a"
rem Check if first char in part is in "digits"
for /F %%b in ("!part:~0,1!") do if "!digits:%%b=X!" neq "%digits%" set "firstDigit=%%b"
)
)
rem Split name at "underscore+firstDigit"
for /F %%a in ("!firstDigit!") do for /F %%b in ("!fname:_%%a= !") do set "folname=%%b"
echo Moving file %%A to folder !folname!
)
Output example:
Moving file a_t_r_a_v__a_83563614_2994781403891160_6736610473828214002_n.jpg to folder a_t_r_a_v__a
Moving file alexa_vn10s1ty429_81834750_1570208686488587_1442450484794514255_n.jp to folder alexa_vn10s1ty429
Moving file shurra.m.lance_96141088_342661823370490_6342263806980952485_n.jpg to folder shurra.m.lance
Moving file anu.m0111_104134441_573701159868969_589828829350351913_n.jpg to folder anu.m0111

Scraping all file names with their creation and modification date from a directory

I am running this bat script, fileNames.bat > print.txt, to get all file Names.
#echo off
setlocal disableDelayedExpansion
:: Load the file path "array"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Access the values
setlocal enableDelayedExpansion
for /l %%N in (1 1 %file.count%) do echo !file.%%N!
However, I would also like to get in the output the creation and modification date for each file path.
My current output looks like that:
C:\Programs\FirefoxPortable\fileNames.bat
C:\Programs\FirefoxPortable\Firefox
C:\Programs\FirefoxPortable\print.txt
C:\Programs\FirefoxPortable\Firefox\App
C:\Programs\FirefoxPortable\Firefox\Data
Any suggestions, how to get the creation and modification date next to each file path?
I appreciate your replies!
Following .bat script produces a csv-like output with | vertical line delimited values of next pattern:
type|creation datetime|modification datetime|full path|
Delayed expansion kept disabled as there are file names containig ! exclamation mark(s): see the call set trick in Variables: extract part of a variable (substring) and in CALLing internal commands.
Note that retrieving creation date and time discriminates between folders and files.
%%G tokenization could vary for another regional settings!
#ECHO OFF
#SETLOCAL enableextensions disableDelayedExpansion
:: Set the working directory and store the previous folder/path
pushd d:\test
:: Load the file path "array"
set "file.count=0"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Restore path/folder most recently stored by the PUSHD command
popd
:: Access the values (keep )
set /A "ii=0"
:loop
if %ii% GEQ %file.count% goto :loopend
set /A "ii+=1"
call set "file.curr=%%file.%ii%%%"
if exist "%file.curr%\" (
rem folder
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:d /t:C "%file.curr%"
') do (
if not defined defined (
echo FLDR^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
) else (
rem file
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:-d /t:C "%file.curr%"
') do (
if not defined defined (
echo FILE^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
)
goto :loop
:loopend
ENDLOCAL
goto :eof
My current output looks as follows (reduced to reasonable size):
==>D:\bat\SO\31824138.bat
FILE|26.07.2015 11:02|26.07.2015 11:02|d:\test\File N.txt|
FLDR|24.07.2015 20:21|05.08.2015 18:44|d:\test\set|
FILE|24.07.2015 20:23|24.07.2015 20:23|d:\test\set\hklm.txt|
FILE|24.07.2015 20:26|24.07.2015 20:30|d:\test\set\regs.txt|
FILE|05.08.2015 18:44|05.08.2015 18:45|d:\test\set\t!exclam!t.txt|
==>

Windows batch file to find duplicates in a tree

I need a batch file ( Windows CMD is the interpreter, a .bat ) to do this type of task:
1) Search through a folder and its subfolders
2) Find files with the same filename and extension ( aka duplicates )
3) Check if they have the same size
4) If same name + same size, echo all the files except the first one ( practically I need to delete all except one copy )
Thanks for any type of help
This is only an initial script, just for check the files, in a folder and its subfolders, and their size:
#Echo off
Setlocal EnableDelayedExpansion
Set Dir=C:\NewFolder
For /r "%Dir%" %%i in (*) do (
Set FileName=%%~nxi
Set FullPath=%%i
Set Size=%%~zi
Echo "!FullPath!" - SIZE: !Size!
)
Echo.
Pause
This script does what you ask. Just set the ROOT variable at the top to point to the root of your tree.
#echo off
setlocal disableDelayedExpansion
set root="c:\test"
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~znxF:%%~fF:)|sort"'
) do (
set "currTest=%%A"
set "currFile=%%B:%%C"
setlocal enableDelayedExpansion
if !currTest! equ !prevTest! echo "!currFile!"
endlocal
set "prevTest=%%A"
)
But you can make the test more precise by using FC to compare the contents of the files. Also, you can incorporate the DEL command directly in the script. The script below prints out the commands that would delete the duplicate files. Remove the ECHO before the DEL command when you are ready to actually delete the files.
#echo off
setlocal disableDelayedExpansion
set root="c:\test"
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~znxF:%%~fF:)|sort"'
) do (
set "currTest=%%A"
set "currFile=%%B:%%C"
setlocal enableDelayedExpansion
set "match="
if !currTest! equ !prevTest! fc /b "!prevFile!" "!currFile!" >nul && set match=1
if defined match (
echo del "!currFile!"
endlocal
) else (
endlocal
set "prevTest=%%A"
set "prevFile=%%B:%%C"
)
)
Both sets of code may seem overly complicated, but it is only because I have structured the code to be robust and avoid problems that can plague simple solutions. For example, ! in file names can cause problems with FOR variables if delayed expansion is enabled, and = in file name causes a problem with npocmoka's solution.
#echo off
setlocal
for /f "tokens=1 delims==" %%# in ('set _') do (
set "%%#="
)
for /r %%a in (*.*) do (
if not defined _%%~nxa%%~za (
set "_%%~nxa%%~za=%%~fa"
) else (
echo %%~fa
)
)
endlocal

How to create a unique output filename for Windows Script?

I am trying to create a windows script that should generate this kind of filename everytime I run it: filename1, filename2, filename3 and so on. Here is what I have so far:
(
#echo off
wmic logicaldisk get size,freespace,caption
) > disk.txt
I hope you can help me. Thanks!!
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
You now have any number of filenames available.
%tempfile%a exists and is empty, but %tempfile%anythingelse should be available for use.
#ECHO OFF
SETLOCAL
SET "basename=filename"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
ECHO %outname%
GOTO :EOF
Ah - increment the destination filename on each run. This should do that. It's not actually creating a file - you'd need to create the file %outname% each time to have it increment...
(the space between %basename% and %outname% is optional, of course - omit it if desired.)
edited to include .txt
This will give you up to 1000 filenames but you can go higher, up to 2 Billion, but the higher you go the longer the delay will be before it picks a filename.
#echo off
for /L %%a in (1,1,1000) do if not defined filename if not exist "filename%%a.txt" set "filename=filename%%a.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%filename%"
#echo off
setlocal enableextensions
call :getNextFilename "filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
call :getNextFilename "%cd%\filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
endlocal
exit /b
:getNextFilename whatToSearch returnVariable
setlocal enableextensions enabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for %%a in ("%~1"
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do if %%~c geq !max! set /a "max=%%c+1"
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
This should find the next file in sequence independently of the existence of holes in the numeration of the files. A path can be included or omitted. The * will be used as the placeholder for the numeration. BUT this will not work if files or included paths have "problematic" characters.
If the date/time of creation of the file can be considered, then this version can be optimized as
:getNextFilename whatToSearch returnVariable
setlocal enableextensions disabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*?" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for /f "delims=" %%a in ('dir /tc /o-d /b "%~1" 2^>nul'
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do set /a "max=%%c+1" & goto done
:done
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
that will take the latest created instance of the file set.
I finally figured out where to put the .txt extension. This is from #Magoo's answer but I wanted the file to be a text file so I placed the .txt twice in order for it to work properly.
#ECHO OFF
SETLOCAL
SET "basename=DISK-OUT"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%outname%"
GOTO :EOF

Getting a list of common strings with first occurance mark

I've got bunch of text files with some content. First I wanted to number the lines globally. Then I extracted all lines that are duplicated somewhere (occur in any of given files at least twice). But now I need to mark all of these lines with the filename and line number of the first occurrence of this line. And now the funny part - it needs to be a windows batch file, using native windows tools. That's why I've got this problem to begin with.
So, to sum it up:
I have a file A with unique strings/lines, each of them is said to occur at least twice in given set of files.
I need to search these files and mark all occurrences of given line from A file with
-file name in which the line first occured
-line number in this file
This is my code with effort to number lines and format files.
#echo off
setlocal EnableDelayedExpansion
set /a lnum=0
if not [%1]==[] pushd %1
for /r %%F in (*.txt) do call :sub "%%F"
echo Total lines in %Files% files: %Total%
popd
exit /b 0
:Sub
set /a Cnt=0
for /f %%n in ('type %1') do (
set /a Cnt+=1
set /a lnum=!lnum!+1
echo ^<!lnum!^> %%n >> %1_ln.txt && echo ^<!lnum!^> >> %1_ln.txt && echo. >> %1_ln.txt
)
set /a Total+=Cnt
set /a Files+=1
echo %1: %Cnt% lines
#echo off
setlocal EnableDelayedExpansion
set lnum=0
if not "%~1" == "" pushd %1
rem "I've got bunch of text files..." (%%F is file name)
for /r %%F in (*.txt) do call :sub "%%F"
echo Total lines in %Files% files: %lnum%
popd
exit /b 0
:Sub "filename"
set Cnt=0
rem "... with some content." (%%n is line contents)
(for /f "usebackq delims=" %%n in (%1) do (
set /a Cnt+=1
rem "First I wanted to number the lines globally."
set /a lnum+=1
echo ^<!lnum!^> %%n
rem "Then I extracted all lines that are duplicated somewhere" (that were defined before)
if defined line[%%n] (
rem "I need to mark all of these lines with the filename and line number of the first occurrence of this line."
echo ^<!line[%%n]!^>
echo/
) else (
REM (Store the first occurrence of this line with *local* line number and filename)
set line[%%n]=!Cnt!: %1
)
)) > "%~PN1_ln.txt"
set /A Files+=1
echo %1: %Cnt% lines
exit /B
The above Batch program ignore empty lines in the input files and fail if they contain special Batch characters, like ! & < > |; this limitation may be fixed if required.
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%s IN (A) DO (
SET searching=Y
FOR /f "delims=" %%f IN (
'dir /s /b /a-d *.txt') DO IF DEFINED searching (
FOR /f "tokens=1delims=:" %%L IN (
'findstr /b /e /n /l /c:"%%s" ^<"%%f"') DO IF DEFINED searching (
ECHO Line %%L IN "%%f" FOUND "%%s"
SET "searching="
)
)
)
Here's the meat of a routine that should do what you appear to be looking for - and that's as clear as mud.
It looks through the "A" file for each string in turn, assigns the string to %%s and sets the flag searching
Then it looks through the file list, assigning filenames to %%f
Then it executes a findstr to find the /c:"%%s" complete string %%s (including any spaces) in /l or literal mode (ie. not using regular expressions) for a line that both /b and /e begins and ends with the target (ie exactly matches) and /n numbers those lines.
The output of findstr will be in the format linenumber:linecontents so if this line is examined by the FORwith the option "delims=:" then the partion up to the first : is assigned to to %%L
So - %%L contains the line#, %%f the filename, %%s the string
Clearing searching having detected this line by setting its value to [nothing] means it's not NOT DEFINED hence no further lines will be reported from the current file, and no further filenames will be examined.
Now if you want to get a listing of ALL of the occurrences of the target lines, all you need to do is to REM-out the SET "searching=" line. Searching will then never be reset, so each line in each file is reported.
If you want some other combination, please clarify.
I have absolutely no idea whatever what you mean by "marking" a line.
#ECHO OFF & setlocal
for /f "tokens=1*delims==" %%i in ('set "$" 2^>nul') do set "%%i="
for %%a in (*.txt) do (
for /f %%b in ('find /v /c "" ^<"%%a"') do echo(%%b lines in %%a.
set /a counter=0, files+=1
for /f "usebackqdelims=" %%b in ("%%~a") do (
set /a counter+=1, total+=1
set "line=%%b"
setlocal enabledelayedexpansion
if not defined $!line! set "$!line!=%%a=!counter!=!line!"
for /f "delims=" %%i in ('set "$" 2^>nul') do (if "!"=="" endlocal)& set "%%i"
)
)
echo(%total% lines in %files% files.
for /f "delims=" %%a in (a) do set "#%%a=%%a"
for /f "tokens=2,3*delims==:" %%i in ('set "$" 2^>nul') do (
if defined #%%k echo("%%k" found in %%i at line %%j.
)
Script can handle !&<>|%, but not =.

Resources