Replacing part of folder name in multiple subdirectories - batch-file

I'm trying to write a batch script to replace the filler XXXX in a given folder structure for future projects. The batch should be put in the folder and be run there.
e.g.:
New_Project_0XXXX
01_0XXXX_Misc
01_0XXXX_Archive
SomeOtherName
02_0XXXX_NewData
MoreOtherStuff
02_0XXXX_OtherStuff
and so on.
The filler is to be replaced with a user prompted project number. I've had this first shot at it, but the problem is, that it renames the first layer and then it can't find the others.
set /P oldString="To Replace:"
set /P newString="Replace with:"
call :rendirs %oldString% %newString%
:rendirs
for /f %%a in ('dir /s /ad /b "*%~1*"') do (call :rename %%a %~1 %~2)
:rename
set oldname=%~n1
call set newname=%%oldname:%~2=%~3%%
ren %oldname% %newname%
I've got the idea of cutting the for loop to 'dir /ad /b' so it doesn't involve the subfolders yet and recursively calling the :rendirs function for the subfolders, but it doesn't work.
:rendirs
for /f %%a in ('dir /ad /b' "*%~1*"') do (
call :rename %%a %~1 %~2
for /f %%i in ('dir /ad /b') do (
cd %%i
call :rendirs %~1 %~2
)
)
How do I make cd work inside the loop or how do I ensure the script can find the subdirectories?

You should use the move command, instead of ren, because it can rename full qualified pathnames.
Then the :rename block looks like
:rename
set oldname=%1
call set newname=%%oldname:%~2=%~3%%
move %old_fullname% %newname%
BUT, you still have the problem of the renaming order.
First the top level directory is renamed, then all others will fail.
To solve this, you could reverse the dir list

I would accomplish your task using the following script (see the explanatory remarks in the code):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (target root directory)
set "_MASK=%~1" & rem // (directory name pattern)
set "_SEARCH=%~2" & rem // (search string)
set "_REPLAC=%~3" & rem // (replacement string)
rem // Skip all actions if no search string is provided:
if defined _SEARCH (
rem /* Traverse directory hierarchy in reverse manner,
rem so lowest level directories come first: */
for /F "eol=| delims=" %%D in ('
dir /S /B /A:D-H-S "%_ROOT%\%_MASK%" ^| sort /R
') do (
rem // Store path and name of iterated directory:
set "DIRP=%%D" & set "NAME=%%~nxD"
rem // Toggle delayed expansion to avoide loss of `!`:
setlocal EnableDelayedExpansion
rem // Actually rename iterated directory here:
ren "!DIRP!" "!NAME:%_SEARCH%=%_REPLAC%!"
endlocal
)
)
endlocal
exit /B
Given it is named renaming.bat, it should be used with the following arguments, based on your sample data and assuming XXXX is to be replaced by 1234:
renaming.bat "*_0XXXX*" "_0XXXX" "_01234"

Related

get last modification date of most recent file (recursive) and add the date to the folder name

i have a folder with subfolders and multiple files.
Root\Mainfolder1\Subfolder1\files
Root\Mainfolder1\Subfolder1\Subfolder2\files
Root\Mainfolder1\Subfolder3\files
Root\Mainfolder2\files
Root\Mainfolder2\Subfolder1\files
and the batchfile is inside the rootdir
i need to get the last modification date (yyyymmdd) for the most recent file throughout all folders.
after getting the date, i need to add the date to the mainfolders name like:
"Mainfolder (yyyymmdd)"
after that i need the same for the next mainfolder. basically i would end up with something like this:
Root\Mainfolder1 (yyyymmdd)
Root\Mainfolder2 (yyyymmdd)
Root\Mainfolder3 (yyyymmdd)
additional info: my system has another date format: dd.mm.yyyy (but i need the above format)
i want this to be a batchfile which i can place inside the root dir and then execute it to do what i have explained above.
what code do i have so far? well near to nothing at all. nothing usable. only thing i found was how to list all modification dates (but they are not sorted or anything) with this:
dir /s /O:D /T:W /A:-D
and i cant find anything through the search function
any suggestions are highly welcomed, thanks :)
#echo off
setlocal EnableDelayedExpansion
rem Process all folders in current dir
for /F "delims=" %%a in ('dir /B /A:D') do (
rem Get the dates of all files in this folder and keep the most recent one
cd "%%a"
set "recent=0"
for /F "tokens=1-3 delims=/. " %%d in ('dir /S /T:W /A:-D ^| findstr "^..\."') do (
if %%f%%e%%d gtr !recent! set "recent=%%f%%e%%d"
)
rem Rename this folder
cd ..
ECHO ren "%%a" "%%a (!recent!)"
)
If the output looks correct, remove the ECHO part in last line.
Give this a try. I put some comments in the code to help you understand what is going on. Remove the ECHO in front of the RENAME command when you are satisfied with the output.
#echo off
setlocal enabledelayedexpansion
REM List the directories first
FOR /F "delims=" %%G IN ('dir /ad /b') DO (
rem Change to the directory
pushd "%%G"
set "dt="
set "newest="
REM Get the date and time from the newest file from the folder.
FOR /F "delims=" %%H IN ('dir /S /a-d /b 2^>nul') DO (
REM by default the ~t modifier uses modified date and time
SET "dt=%%~tH"
REM Now split up the date into a usable format.
REM Change the delims option to the separator your date format uses.
REM also need a space because the time follows the date
FOR /F "tokens=1-3 delims=/. " %%I IN ("!dt!") DO (
set "dt=%%K%%J%%I"
)
IF NOT DEFINED newest IF defined dt set "newest=!dt!"
IF DEFINED newest IF defined dt IF !dt! GTR !newest! set "newest=!dt!"
)
POPD
IF DEFINED newest echo rename "%%~G" "%%G(!newest!)"
)
Here is a batch file that determines the lastest last modification date throughout a given directory tree; it depends on the local date format settings, unfortunately:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~1" & rem // (take the first command line argument as the root dir.)
rem // Define sub-string portions of the local date format -- see `date /T`:
set "_Y=0,4" & rem // (year: zero-based start char. index, length in number of chars.)
set "_M=5,2" & rem // (month:zero-based start char. index, length in number of chars.)
set "_D=8,2" & rem // (day: zero-based start char. index, length in number of chars.)
rem // Ensure `_ROOT` not to be empty:
if not defined _ROOT set "_ROOT=."
set "LAST=00000000"
for /F "delims= eol=|" %%D in ('echo "%_ROOT%"^& dir /B /S /A:D "%_ROOT%" 2^> nul') do (
call :GETLAST FILE "%%~D" && (
call :GETDATE FILE "%%FILE%%"
setlocal EnableDelayedExpansion
if !LAST! LSS !FILE! (
endlocal
call set "LAST=%%FILE%%"
) else (
endlocal
)
)
)
rem // Return resulting last modification date within entire directory tree:
echo(%LAST%
endlocal
exit /B
:GETLAST rtn_path val_dir
::Get last modified file in the given directory.
::PARAMETERS:
:: rtn_path variable to receive path to last modified file;
:: val_dir directory path to search last modified file in;
::ERRORLEVEL: 1 if no file has been found, and 0 otherwise;
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "ITEM=%~2"
(for /F "delims= eol=|" %%F in ('dir /B /A:-D /T:W /O:-D "%ITEM%\*.*" 2^> nul') do (
set "ITEM=%ITEM%\%%F"
(call ) & goto :QUIT
)) || set "ITEM="
:QUIT
endlocal & set "%#RTN%=%ITEM%"
exit /B
:GETDATE rtn_date val_path
::Get last modification date of file/dir., properly formatted like `YYYYMMDD`.
::PARAMETERS:
:: rtn_date variable to receive formatted date;
:: val_path path to get last modification date of;
setlocal DisableDelayedExpansion
set "#RTN=%~1"
set "ITEM=%~t2"
setlocal EnableDelayedExpansion
set "ITEM=!ITEM:~%_Y%!!ITEM:~%_M%!!ITEM:~%_D%!"
endlocal & endlocal & set "%#RTN%=%ITEM%"
exit /B

Batch script to pick filename from a text file and find the latest file

Scenario:
We have multiple releases of a product, and for each release, a folder is created in the main folder. A help file is modified in various releases. I have all the help file names listed in a text file.
I need a script to:
Take each file name from the filenames.txt file
Search for the file by that name in the entire directory (in all releases)
Find the latest file
Copy it to a specified folder
I took help from the various pieces of code I found on Stack Overflow, and combined them to get this code:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
echo.
FOR /F "usebackq delims=" %%a in ("filenames.txt") do (
SET "x=%%a"
ECHO '!x!'
SET FFPath=C:\SVN\nlbavwdocsvn\rep_doc_erpln\trunk\ERPLN
SET NewPath=C:\Lavanya\extracted
SET NewestDate=20160824
ECHO Recursively searching %FFPath%
FOR /F %%I in ('DIR %FFPath%\ !x! /a:-d /s /b') DO (
SET FullDate=%%~tI
ECHO %FFPath%
REM Set CurrDate to yyyymmdd format. Note: Will fail if regional settings changed.
SET CurrDate=!FullDate:~6,4!!FullDate:~0,2!!FullDate:~3,2!
If !CurrDate! gtr !NewestDate! (
SET NewestDate=!CurrDate!
SET NewestFile=%%~fI )
ECHO Copying %NewestFile% to %NewPath%
ECHO.
COPY /Y "%NewestFile%" "%NewPath%"
ECHO.
)
)
PAUSE
This code is not working. And I am unable to figure out the error.
Here is a script to search for the most recently modified file, using the wmic command to retrieve the last modification date/time in a locale-independent manner (e. g., 20160824115500.000000+060).
So for every file name read from the list file .\filenames.txt, the directory tree routed at directory C:\SVN\nlbavwdocsvn\rep_doc_erpln\trunk\ERPLN is searched for matching files recursively, and the respective modify date/time stamp is gathered. Due to its format, a simple greater-than (GTR) comparison can be done do determine whether or not it is a later point of time than a cached one; if the criterion is fulfilled, the cache is updated accordingly.
The upper-case REM and ECHO commands constitute placeholders only for the real action to be performed on the files. Extend the script there as you like. Variable !LASTFILE! holds the full path to each encountered file.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "LOCATION=C:\SVN\nlbavwdocsvn\rep_doc_erpln\trunk\ERPLN"
set "FILELIST=.\filenames.txt"
set "WMICPROP=LastModified" & rem // {CreationDate | LastAccessed | LastModified}
pushd "%LOCATION%" || exit /B 1
for /F "usebackq eol=| delims=" %%L in ("%FILELIST%") do (
set "LASTFILE="
set "LASTFAGE=00000000000000.000000+000"
for /F "eol=| delims=" %%F in ('dir /B /S /A:-D "%%~L"') do (
set "FILE=%%F"
setlocal EnableDelayedExpansion
set "FILE=!FILE:\=\\!"
for /F "tokens=2 delims==" %%J in ('
2^> nul wmic DataFile WHERE ^(Name^="!FILE!"^) GET %WMICPROP% /VALUE ^|^| ^
2^> nul wmic DataFile WHERE Name^="!FILE!" GET %WMICPROP% /VALUE
') do for /F %%I in ("%%J") do (
endlocal
set "FAGE=%%I"
setlocal EnableDelayedExpansion
if !FAGE! GTR !LASTFAGE! (
endlocal
set "LASTFILE=%%F"
set "LASTFAGE=%%I"
setlocal EnableDelayedExpansion
)
)
endlocal
)
if defined LASTFILE (
setlocal EnableDelayedExpansion
REM Do whatever you want with the file here...
ECHO Newest file: "!LASTFILE!"
endlocal
)
)
popd
endlocal
exit /B

copy & rename files to other folder

I have some of log files formatted like this "name.log"
I would like to copy those from one folder to another folder like
xcopy /y "C:\Folder1" "D:\Folder2"
And i need to rename file with created date of original file (no copy file) so that the text file in Folder2 would be like "name yyyymmddhhmm.log" if some file has the same name (date of creation) it will be overwritten.
The code:
set Source=C:\Users\user1\Desktop\Folder1
set Dest=D:\Folder2
if not exist %Dest% md %Dest%
for /F %%a in ('dir /b "%Source%\*.txt"') do call :Sub %%a
goto :eof
:Sub
set "filename=%1"
for /F %%s in ("%Source%\%1") do if %%~zs==0 goto :eof
set "datepart="
FOR /F "tokens=1-5 delims=/-: " %%a IN ('dir /tc "%filename%" ^| findstr "%filename%"') DO (
IF "%%c" neq "" SET "datepart=%%c%%a%%b%%d%%e"
)
FOR /F %%a IN ("%filename%") DO (
set "NewName=%%~na %datepart%%%~xa"
)
xcopy /y "%Source%\%filename%" "%Dest%\%NewName%*"
GOTO :EOF
The problem is that If I don't put the .bat in the same folder that origin files (Folder1),the files aren't change name. For example, if it is out, the files change name with old name and one white space.
The command windows tell me that it doesn't find the file when it get the creation date.
If I put the script into folder1 it works well.
On the other hand, if I execute the script with "Task Scheduler" I have the same problem. The files are copied but without date of creation.
What do I need to solve this problem?
#ECHO OFF
SETLOCAL
set Source=C:\Users\user1\Desktop\Folder1
set Dest=D:\Folder2
set "Source=u:\sourcedir\t w o"
set "Dest=u:\destdir"
if not exist "%Dest%" md "%Dest%"
for /F "delims=" %%k in ('dir /b "%Source%\*.log"') do call :Sub "%%k"
goto :eof
:Sub
SET "newname=%~1"
for /F "delims=" %%s in ("%Source%\%~1") do (if %%~zs==0 goto :eof
FOR /F "tokens=1-5 delims=/-: " %%a IN ('dir /tc "%Source%\%~1" ^| findstr "%~1"') DO (
IF "%%c" neq "" SET "newname=%%~ns %%c%%a%%b%%d%%e%%~xs"
)
)
ECHO(xcopy /y "%Source%\%~1" "%Dest%\%NewName%"
GOTO :EOF
The required XCOPY commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(XCOPY to XCOPY to actually copy the files. Append >nul to suppress report messages (eg. 1 file copied)
This may seem quite a radical change, but actually it really isn't.
First issue is that I overrode your directory settings with directories to suit my system. The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can safely be used "quoteless".
Using quotes in the md command makes the procedure immune to "paths containing separators" - I test using spaces in paths and filenames because that appears to be a popular thing to do.
I changed the directory-scan metavariable from %%a to %%k to avoid confusion with the %%a in the subroutine. Your text says that you are starting with &.log files, but your filemask was *.txt so I changed it to *.log. Quoting the parameter delivered to :Sub means the procedure will receive the entire name of the file if it contains spaces.
Within the subroutine, it would appear that yowant no name-change if the %%c part from the dir/tc scan is empty. %~1 is the supplied filename minus the quotes.
The outer loop in %%s : I added delims= to cater for spaces in filenames and used %~1 in preference to %filename%
Within the %%s block, %%s refers to the file, so you can use %%s and its modified forms like %%~zs to refer to that file's characteristics - which unfortunately do not include create-date (%%~ts contains the last-update date - you may be able to use that in te following line rather than dir and findstr)
Then as #aschipfi suggested, include the source directory in the dir otherwise the dir takes place on the current directory.
FOR /F "tokens=1-5 delims=/-: " %%a IN ("%%~ts") DO (
should work for you if you can use last-update-date in place of create-date.
So - if %%c is not empty, set the new name to (the name part of the file in %%s)+space+the date string+(the extension in %%s)
And then do the xcopy - using the old name unless it was modified.

create folders based on string in file name

I need a batch file to create a process for a list of files in a directory.
The filename structure is, for example: 00000_AAA_132144_2012021.txt
I need the batch to:
1 - Create a folder name based on the numbers after the second underscore, as this is the only constant in the naming.
2 - Move the file into the new folder.
In the example of the above the batch would create a folder called 132144 and then move the file 00000_AAA_132144_2012021.txt into the folder
For a similar requirement I used the script Endoro created for me (below). Is it possible to modify this to meet my requirement?
#echo off &setlocal
for /f "delims=" %%i in ('dir /b /a-d *.PDF') do (
set "filename1=%%~i"
setlocal enabledelayedexpansion
set "folder1=!filename1:~11,6!"
mkdir "!folder1!" 2>nul
move "!filename1!" "!folder1!"
endlocal
)
If you know that the filenames will be the same length, you can do the following to get the numbers after the second underscore -
set filename=00000_AAA_132144_2012021.txt
set dirname=%filename:~10,6%
If the spacing may vary - you can do the following -
for /f "delims=_ tokens=3" %%a in ('echo %filename%') do set dirname=%%a
And yes, the script written for you seems to do essentially the same thing as what you're asking - I've edited it to do what you've asked -
#echo off
setlocal enabledelayedexpansion
for /f %%i in ('dir /b /a-d *.txt') do (
set "filename=%%~i"
for /f "delims=_ tokens=3" %%a in ('echo !filename!') do set folder=%%a
mkdir "!folder!" 2>nul
move "!filename!" "!folder!"
)
This will move all *.txt documents to a folder created based on the third section of the text files name. Note that this will cause problems if you have .txt documents in the directory that do not follow the same naming standard.
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET "destdir=c:\destdir"
FOR /f "delims=" %%a IN ('dir /b /a-d "%sourcedir%\*_*_*_*.txt" ') DO (
FOR /f "tokens=3delims=_" %%m IN ("%%a") DO (
ECHO MD "%destdir%\%%m"
ECHO MOVE "%sourcedir%\%%a" "%destdir%\%%m\"
)
)
GOTO :EOF
Endoro's routine selects .pdf files, you've specified .txt
Find filenames matching the mask, find the third _-separated token in the name, make that directory and then move the file.
The required commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MD to MD to actually create the directories and change ECHO MOVE to MOVE to actually move the files.
Append 2>nul to suppress error messages (eg. when the directory already exists)
Append >nul to suppress report messages (eg. 1 file moved)

Unflattening a directory structure based on filenames

This is the inverse of the "flatten" operation described in this question: Flattening a directory
I would like a batch script that will go through each file in a "flattened" directory and put them back into their original directories, creating the directories as necessary
So if the following files were in my folder:
images-nature-dcim001.jpg
images-nature-dcim002.jpg
images-dcim003.jpg
images-indoors-dcim004.jpg
It would produce the resulting directory structure, creating the directories and moving (or copying) the files into the correct folder.
images
dcim003.jpg
nature
dcim001.jpg
dcim002.jpg
indoors
dcim004.jpg
Note: the example uses hyphens to separate the directories, but they can be any character.
This works here. It creates the four files at the top and then moves them.
#echo off
type nul >images-nature-dcim001.jpg
type nul >images-nature-dcim002.jpg
type nul >images-dcim003.jpg
type nul >images-indoors-dcim004.jpg
for %%a in (*.jpg) do call :routine "%%a"
pause
goto :eof
:routine
set "a=%~1"
set "b=%a:-=\%"
for %%b in ("%b%") do (
md "%%~pb" 2>nul
move "%a%" "%%~pb\%%~nxb"
)
#ECHO OFF &SETLOCAL
FOR /f "delims=" %%a IN ("%cd%") DO SET "precur=%%~dpa"
FOR /f "delims=" %%a IN ('dir /b /s /a-d *.txt') DO (
SET "fname=%%~fa"
SETLOCAL ENABLEDELAYEDEXPANSION
SET "nname=!fname:%precur%=!"
SET "nname=!nname:\=-!"
ECHO REN "!fname!" "!nname!"
ENDLOCAL
)
Here's the version I am using based on foxidrive's approach to getting the directory name. I didn't think of simply replacing delimiters with back-slashes.
#echo off
Setlocal EnableDelayedExpansion
rem // Directory Unflatten
rem // recursively unflattens directories
rem // and prepends the directory name to
rem // the filename
rem // Configuration options
rem // * Files to search for
set pattern=*jpg;*.png
rem // * Directory name delimiter
set delim=-
rem // Perform moving
for %%X in (%pattern%) do (
set A=%%X
rem // Replace delimiter with back-slash
set b=!A:%delim%=\!
rem // Not sure how to clean this up
for %%B in ("!b!") do (
if not exist %%~pB (
md "%%~dpB"
)
move "!A!" "%%~dpB%%~nxB"
)
)

Resources