Script to archive folder and rename files

2

0

My goal is to have a .bat that I can use to automatically archive folders via copy, rename the original folder and the files within with the current date and then clean out old production files.

  1. Copy target folders to the "Completed" directory
  2. Update date on the source folders using the format Previous Name_Today's Date (MMDDYY)
  3. Update the date of the INDD files within the source folder using the format Current Name_Today's Date (MMDDYY)
  4. "Clean" the source folder by deleting all .VPS and .PDFs files within the source folder

I am a novice but I have pieced together this code from research and sources:

@echo off
setlocal enabledelayedexpansion
for /f "skip=1" %%x in ('wmic os get localdatetime') do if not defined MyDate set MyDate=%%x
set today=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%
xcopy /s /e /q /y "G:\...\Annual_*" "G:\...\_DONE\"
xcopy /s /e /q /y "G:\...\Life_*" "G:\...\_DONE\"
MOVE "G:\...\Annual_*" "G:\...\Annual_today"
MOVE "G:\...\Life_today" "G:\...\Life_today"
FOR /M *.indd /C "cmd /c rename @file \"@fname - today.indd\""
del /s "G:\...\Annual_today" *.pdf
del /s "G:\...\Annual_today" *.vps
del /s "G:\...\Life_today" *.pdf
del /s "G:\...\Life_today" *.vps
"G:\...\New_Job.bat" > output.txt

My end goal is to be able to change the directory path for the source and archive folders so I can reuse this script for different clients files.


Current Issues

As it stands the script doesn't copy and create an archive folder files and just, deletes all VPS and PDF files from all directories, not just the target folder.

I'm not sure if I'm performing the date check correctly to then use it as a variable to rename future folders and files.

I don't know if FOR /M *.indd /C "cmd /c rename @file \"@fname - today.indd\"" is correct to rename the files. The names are a PO number (6digits) then a title, underscore and then the date.

123456_Life_Kit_020819 for example.


Help would be greatly appreciated!


Here's an Example of what the script should do

enter image description here

Copies the WHOLE folder to the _OLD/Archive folder for each one. Then renames the folder and the contents extensions to the "current date". Then deletes the .pdf and .vps files in the NEW date directories only.

Here is an example of the folder structure.

Main directory:

enter image description here

Inside one of the subdirectories:

enter image description here

The only things I'm trying to rename are the MAIN directory folders with the dates (after copying) and then the files within the subdirectory.

No other folders need to be renamed.

Ovaryraptor

Posted 2019-02-08T18:40:35.057

Reputation: 97

1It's a little hard for me to clearly read what directories exist and where do you want to move your files. can you create an example folder structure how it looks like before the script runs and a folder structure how it should look like after it ran? And also if we should backup all files or just certain extensions like .indd. Also, would PowerShell be ok instead of batch? Maybe someone is able to help you in batch, but I can only do PowerShell. I think this is a bit easier to achieve in PowerShell really. – SimonS – 2019-02-11T15:18:29.590

@SimonS Sure thing! I updated my post with a video and more description. – Ovaryraptor – 2019-02-11T18:34:14.987

two last questiosn - what should we do with the folders and files that do not have a _mmddyy at the end of their name? leave them like they are or just add _mmddyy ? also, is the _OLD folder always in the same folder as the work folder you want to back up? – SimonS – 2019-02-12T11:45:08.563

1An example of what this script is supposed to do, would be great it it was textual rather than an image. – harrymc – 2019-02-12T12:01:20.523

@SimonS Powershell is fine I was just working in .bat because of my limited knowledge. I updated my post with images of the folder structures I'm working with. – Ovaryraptor – 2019-02-12T17:43:37.233

@Ovaryraptor Did you test the PowerShell scripts provided on either of the answers yet. If so, I was curious if either of those helped solve the problem or if you still need further help with some script logic for the job? – Pimp Juice IT – 2019-02-14T14:26:59.770

robocopy can do amazing things with one line of code. https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy

– HackSlash – 2019-02-14T20:35:19.993

@PimpJuiceIT So far neither of the posted solutions are working for me. – Ovaryraptor – 2019-02-14T21:08:16.507

@HackSlash I looked at robocopy initially but the issue lies with renaming the internal files and clearing out the unwanted file types. – Ovaryraptor – 2019-02-14T21:08:54.113

@PimpJuiceIT It's not just a 1-day increase, it varies. It could be, 021419 or 021919 or 022219 for the next job etc. The archive would be the previous job+all files with MMDDYY. The dates on the files matter because I potentially have to audit files if something goes awry and I need to cross-reference the current run with past runs. Having the _MMDDYY on the .indd files is very necessary – Ovaryraptor – 2019-02-14T21:22:48.130

1@PimpJuiceIT The archive directory (DONE) never needs to be touched. The starting folder layout is a _DONE directory, Annual_021419, Life_021419. Those 2 folders need to be copied into the _DONE and then the folder _and the files within need to be updated from the *_021419 to the current date (pretend I'm running this on 022219). Does that help clear things up? – Ovaryraptor – 2019-02-14T22:01:47.697

@Ovaryraptor ... There you go, check out my posted answer (and comment below it) when you get a chance. Consider it a special delivery just for you from me. I hope you can put it to use, but the PowerShell was not too bad; I tried to keep it as simple as possible logic wise. In any event, you can run it like a pure batch script to do what's needed as per my interpretation, understand, and extensive testing. – Pimp Juice IT – 2019-02-15T03:43:21.907

@PimpJuiceIT yah, that's probably the problem with my answer. too flexible logic wise, that's why it's hard to understand... Even though i tested every scenario, and it works like a charm. I hope my comments will make it a bit clearer – SimonS – 2019-02-15T08:49:35.050

I think our scripting may be okay but we have a new question about executing scripts on a server/nas drive and getting the folder targeting correct. – Brian – 2019-02-15T19:27:06.737

I think both answers are great and already +1 both of them before I added my answer by the way since I liked them so much. Here's both screen shots to prove it too by the way https://i.imgur.com/2jcBuNf.png and https://i.imgur.com/NrBvpfp.png as you will see both have a blue upvote from me since I'm the one that voted them they are blue. I only added my solution after hearing the OP was having trouble hoping the PowerShell batch hybrid solution would help solve the problem. The issue may also be with the version of PowerShell too but still have some troubleshooting to do. Thanks guys!!

– Pimp Juice IT – 2019-02-15T19:56:43.610

Answers

3

I've included a batch script below that is sort of hybrid since it uses PowerShell but it dynamically builds and executes it but the archive destination per the dest= variable value is used for processing accordingly.

I did use an approach with Robocopy to exclude the file extension types that you want recursively deleted from the destination/archive folder so it simply doesn't copy unnecessarily and thus no need to delete.

I used a whole bunch of PowerShell cmdlets and such so rather than list them all out, I've included some of those in the Further Resources section for additional learning if you desire to understand further.

Batch Script

Note: Just set the the source, destination, and excluded files per the correlated variables (src=, dest=, and excludedFiles=) at the top of the script below then simply click to run.

SET "src=G:\Folder\Production"
SET "dest=G:\Folder\__Archive"
SET "fname=*.*"
SET "excludedFiles=*.pdf *.vps"

Robocopy "%src%" "%fname%" "%dest%" /E /XF %excludedFiles%
CALL :PSScript

SET PowerShellDir=C:\Windows\System32\WindowsPowerShell\v1.0
CD /D "%PowerShellDir%"
Powershell -ExecutionPolicy Bypass -Command "& '%PSScript%'"
IF EXIST "%PSScript%" DEL /Q /F "%PSScript%"
EXIT

:PSScript
SET PSScript=%temp%\~tmp%~n0.ps1
IF EXIST "%PSScript%" DEL /Q /F "%PSScript%"

ECHO $Main ^= "%dest%";                                                                                                               >"%PSScript%"
ECHO $Today ^= ^(^("{0:MMddyy}" -f ^(get-date^).AddHours^(0^)^).ToString^(^)^)                                                        >>"%PSScript%"
ECHO $Folders ^= ^(Get-ChildItem -Directory $Main ^| ^? {$_ -match "([0-9]{6})"}^);                                                   >>"%PSScript%"

ECHO $Folders ^| %% {                                                                                                                 >>"%PSScript%"
ECHO If^($_ -match "([0-9]{6})"){                                                                                                     >>"%PSScript%"
ECHO         $root  ^= ^(Split-Path -path $_.Fullname^);                                                                              >>"%PSScript%"
ECHO         $oBase ^= ^(Split-Path -path $_.Fullname -leaf^);                                                                        >>"%PSScript%"
ECHO         $nBase ^= ^($oBase.Replace^($matches[1],$Today^)^);                                                                      >>"%PSScript%"
ECHO         Rename-Item "$root\$oBase" "$root\$nBase";                                                                               >>"%PSScript%"
ECHO     }                                                                                                                            >>"%PSScript%"
ECHO };                                                                                                                               >>"%PSScript%"

ECHO $Folders ^= ^(Get-ChildItem -Directory $Main ^| ^? {$_.Name -match "([0-9]{6})"}^).FullName;                                     >>"%PSScript%"
ECHO $Files   ^= ^($Folders ^| %% {Get-ChildItem "$_\*" -File -Include *.indd} ^| ^? {$_.Name -match "[0-9]{6}.*?([0-9]{6})"}^);      >>"%PSScript%"

ECHO $Files ^| %% {                                                                                                                   >>"%PSScript%"
ECHO If^($_.Name -match "[0-9]{6}.*?([0-9]{6})"^)                                                                                     >>"%PSScript%"
ECHO     {                                                                                                                            >>"%PSScript%"
ECHO         $x ^= $matches[1];                                                                                                       >>"%PSScript%"
ECHO         $root ^= ^(Split-Path -path $_.Fullname^);                                                                               >>"%PSScript%"
ECHO         $nName ^= ^($_.Name.Replace^($x,$today^)^);                                                                              >>"%PSScript%"
ECHO         If^(!^(Test-Path "$root\$nName"^)^){Rename-Item $_.FullName "$root\$nName"};                                             >>"%PSScript%"
ECHO     }                                                                                                                            >>"%PSScript%"
ECHO };                                                                                                                               >>"%PSScript%"
GOTO :EOF

PowerShell Logic

Note: This is the PowerShell alone in case you want to use it, but you simply set the $Main = variable value to be that of the archive folder path of the folders and files with the mmddyy string that gets updated with those characters of the current date when run.

$Main = "G:\Folder\__Archive";                                                                                                                
$Today = (("{0:MMddyy}" -f (get-date).AddHours(0)).ToString())                                                          
$Folders = (Get-ChildItem -Directory $Main | ? {$_ -match "([0-9]{6})"});                                                   
$Folders | % {                                                                                                                    
If($_ -match "([0-9]{6})"){                                                                                                   
        $root  = (Split-Path -path $_.Fullname);                                                                                
        $oBase = (Split-Path -path $_.Fullname -leaf);                                                                          
        $nBase = ($oBase.Replace($matches[1],$Today));                                                                        
        Rename-Item "$root\$oBase" "$root\$nBase";                                                                                 
    }                                                                                                                              
};                                                                                                                                 
$Folders = (Get-ChildItem -Directory $Main | ? {$_.Name -match "([0-9]{6})"}).FullName;                                     
$Files   = ($Folders | % {Get-ChildItem "$_\*" -File -Include *.indd} | ? {$_.Name -match "[0-9]{6}.*?([0-9]{6})"});
$Files | % {                                                                                                                      
If($_.Name -match "[0-9]{6}.*?([0-9]{6})")                                                                                    
    {                                                                                                                              
        $x = $matches[1];                                                                                                         
        $root = (Split-Path -path $_.Fullname);                                                                                 
        $nName = ($_.Name.Replace($x,$today));                                                                                
        If(!(Test-Path "$root\$nName")){Rename-Item $_.FullName "$root\$nName"};                                                             
    }                                                                                                                              
};    

PowerShell Version 2.0 Compatible Logic

Batch (PS 2.0)

Note: Just set the the source, destination, and excluded files per the correlated variables (src=, dest=, and excludedFiles=) at the top of the script below then simply click to run.

SET "src=G:\Folder\Production"
SET "dest=G:\Folder\__Archive"
SET "fname=*.*"
SET "excludedFiles=*.pdf *.vps"

Robocopy "%src%" "%fname%" "%dest%" /E /XF %excludedFiles%
CALL :PSScript

SET PowerShellDir=C:\Windows\System32\WindowsPowerShell\v1.0
CD /D "%PowerShellDir%"
Powershell -ExecutionPolicy Bypass -Command "& '%PSScript%'"
IF EXIST "%PSScript%" DEL /Q /F "%PSScript%"
EXIT

:PSScript
SET PSScript=%temp%\~tmp%~n0.ps1
IF EXIST "%PSScript%" DEL /Q /F "%PSScript%"

ECHO $Main ^= "%dest%";                                                                                                                                >"%PSScript%"
ECHO $Today ^= ^(^("{0:MMddyy}" -f ^(get-date^).AddHours^(0^)^).ToString^(^)^);                                                                        >>"%PSScript%"
ECHO $Folders ^= ^(Get-ChildItem $Main ^| ^? {^($_.PSIsContainer^) -and ^($_ -match "([0-9]{6})"^)}^);                                                 >>"%PSScript%"
ECHO $Folders ^| %% {                                                                                                                                  >>"%PSScript%"
ECHO If^($_ -match "([0-9]{6})"^){                                                                                                                     >>"%PSScript%"
ECHO         $root  ^= ^(Split-Path -path $_.Fullname^);                                                                                               >>"%PSScript%"
ECHO         $oBase ^= ^(Split-Path -path $_.Fullname -leaf^);                                                                                         >>"%PSScript%"
ECHO         $nBase ^= ^($oBase.Replace^($matches[1],$Today^)^);                                                                                       >>"%PSScript%"
ECHO         Rename-Item "$root\$oBase" "$root\$nBase";                                                                                                >>"%PSScript%"
ECHO     }                                                                                                                                             >>"%PSScript%"
ECHO };                                                                                                                                                >>"%PSScript%"
ECHO $Folders ^= ^(Get-ChildItem $Main ^| ^? {^($_.PSIsContainer^) -and ^($_ -match "([0-9]{6})"^)}^);                                                 >>"%PSScript%"
ECHO $Files   ^= ^($Folders ^| %% {Get-ChildItem $_.FullName -Recurse -Include *.indd ^| ^? {^(!$_.PSIsContainer^) -and ^($_.Name -match "[0-9]{6}.*?([0-9]{6})"^)}}^);>>"%PSScript%"
ECHO $Files ^| %% {                                                                                                                                    >>"%PSScript%"
ECHO If^($_.Name -match "[0-9]{6}.*?([0-9]{6})"^)                                                                                                      >>"%PSScript%"
ECHO     {                                                                                                                                             >>"%PSScript%"
ECHO         $x ^= $matches[1];                                                                                                                        >>"%PSScript%"
ECHO         $root ^= ^(Split-Path -path $_.Fullname^);                                                                                                >>"%PSScript%"
ECHO         $nName ^= ^($_.Name.Replace^($x,$today^)^);                                                                                               >>"%PSScript%"
ECHO         If^(!^(Test-Path "$root\$nName"^)^){Rename-Item $_.FullName "$root\$nName"};                                                              >>"%PSScript%"
ECHO     }                                                                                                                                             >>"%PSScript%"
ECHO };                                                                                                                                                >>"%PSScript%"      
GOTO :EOF

PowerShell (PS 2.0)

Easy Execute Note: Save this as a text file with a .ps1 extension to a folder such as G:\Folder\Archiver.ps1 and then from the PowerShell command line put a dot, a single blank space, and then the full script name and path enclosed by double quotes press Enter.

enter image description here

$Main = "G:\Folder\__Archive";                                                                                                                
$Today = (("{0:MMddyy}" -f (get-date).AddHours(0)).ToString());  
$Folders = (Get-ChildItem $Main | ? {($_.PSIsContainer) -and ($_ -match "([0-9]{6})")});

$Folders | % {                                                                                                                    
If($_ -match "([0-9]{6})"){                                                                                                   
        $root  = (Split-Path -path $_.Fullname);                                                                                
        $oBase = (Split-Path -path $_.Fullname -leaf);                                                                          
        $nBase = ($oBase.Replace($matches[1],$Today));                                                                        
        Rename-Item "$root\$oBase" "$root\$nBase";                                                                                 
    }                                                                                                                              
};   

$Folders = (Get-ChildItem $Main | ? {($_.PSIsContainer) -and ($_ -match "([0-9]{6})")});
$Files   = ($Folders | % {Get-ChildItem $_.FullName -Recurse | ? {(!$_.PSIsContainer) -and ($_.Name -match "[0-9]{6}.*?([0-9]{6})")}});

$Files | % {                                                                                                                      
If($_.Name -match "[0-9]{6}.*?([0-9]{6})")                                                                                    
    {                                                                                                                              
        $x = $matches[1];                                                                                                         
        $root = (Split-Path -path $_.Fullname);                                                                                 
        $nName = ($_.Name.Replace($x,$today));                                                                                
        If(!(Test-Path "$root\$nName")){Rename-Item $_.FullName "$root\$nName"};                                                             
    }                                                                                                                              
};    

Further Resources

Pimp Juice IT

Posted 2019-02-08T18:40:35.057

Reputation: 29 425

Wow thank you for linking the resources too! I'm having trouble getting your solution to do anything. I've set my paths but nothing occurs. Even running as an administrator the window just opens and closes instantly. I'm running this on a server, could that be causing some issues? – Ovaryraptor – 2019-02-15T16:32:39.683

I'm running the batch. And I should say that it's a network drive, not a server. – Ovaryraptor – 2019-02-15T17:58:33.233

I'm running Windows 7 Professional Service Pack 1 – Ovaryraptor – 2019-02-15T18:47:01.740

@Ovaryraptor You are running PowerShell 2.0 which is rather old so this confirms my suspicion. If you want a really easy, simple, safe and secure Microsoft Windows solution to this problem, simply download and install this: https://www.microsoft.com/en-us/download/details.aspx?id=54616.... After this completes, reboot the machine, and then retry the script batch solution I provided and all should be working as expected. You can run those $PSVersionTable commands afterwards if you wish but this will get you up-to-date and able to program logic much more robustly.

– Pimp Juice IT – 2019-02-15T21:22:01.343

@Ovaryraptor In case though, the WMF has one prerequisite which is installing https://www.microsoft.com/en-us/download/details.aspx?id=42642 to bring your .Net Framework up to version as well. This is all still native, safe, and secure. Read more here too https://docs.microsoft.com/en-us/powershell/wmf/5.1/install-configure. Don't forget to reboot in the name of thoroughness after everything installs before you test out the click-to-run and set your own source and dest variables batch script.

– Pimp Juice IT – 2019-02-15T21:23:21.333

I'm currently running .NET 4.7.2 and I get "The Update is not Applicable to your Computer" This occurs no matter which WMF (3.0-5.1) I try :/ – Ovaryraptor – 2019-02-15T21:59:10.137

@Ovaryraptor Rather than step you through troubleshooting this new issue with getting Windows 7 to install WMF 5.1 or whatever, I just tested the logic in PS version 2.0 myself and made adjustments to cmdlet parameters and such and filtered via PS 2.0 compatible commands, etc. Look over the Batch and PowerShell logic in the new bonus section of my answer named PowerShell Version 2.0 Compatible Logic. I hope you find good use of it so you can move forward with your task as-is but when you do upgrade the newer logic will still be there for you to use if needed. – Pimp Juice IT – 2019-02-17T00:33:32.113

The 2.0PS version works! Sort of. It actually just copied my whole G:\Prep\Client__Job_VPS directory into a new folder and named that _DONE and then performed the deletions and renames. – Ovaryraptor – 2019-02-18T18:03:09.847

1@Ovaryraptor I'm glad to hear you got some good usefulness out of it. For the most part the logic does what I think you need, but yes, you can fine tune and adjust accordingly for your more specific needs. Feel free to accept the answer if it helped you solve the problem. Happy to help further when I get a chance but it'll be a good 6-8 hours due to other things I got going on right now. – Pimp Juice IT – 2019-02-18T19:26:09.293

1

I have a powershell solution for you.

archiver.ps1, archiver.bat, and command shell line to kick it off. Place the PS1 file and the BAT file in the same folder (anywhere you choose.) The renaming aims for an underscore and 6 digits "_######".

The BAT file takes 4 parameters:

  1. your root folder that you want your recursive search based off of.

  2. the file/folder name subsection to modify Example: "Annual_"

  3. the second file/folder name subsection to modify Example: "Life_"

  4. the name of your archive folder to be made/added to. Example: "_OLD"

File Archiver.ps1:

Param(
     [Parameter(Position=0)] [string]$rootFolder = "C:\scripting\archiver"

     ,[Parameter(Position=1)] [string]$folderName1 = "ANNUAL_"

     ,[Parameter(Position=2)] [string]$folderName2 = "LIFE_"

     ,[Parameter(Position=3)] [string]$archiveFolder = "_OLD"
)
BEGIN
{

$today = get-date -UFormat %m%d%y

$folderName1 = "*"+$folderName1+"*"

$folderName2 = "*"+$folderName2+"*"

$startFolders = GCI -Path $rootFolder -Recurse -Include $folderName1, $folderName2 -Exclude $archiveFolder -Directory

pushd
foreach ($fold in $startFolders){
    cd $fold
    if(-not (Test-Path $archiveFolder)){md $archiveFolder | Out-Null}
    $theseFilez = gci -File
    if($theseFilez){
        foreach ($filez in $theseFilez){
            Copy-Item $filez $archiveFolder -Force
            if($filez -like "*.indd" -and $filez -notlike "*_$today*"){$filez | Rename-Item -NewName {$filez.Name -replace '[_]\d{6}',('_'+$today)}}
        }
    Remove-Item "*.pdf"
    Remove-Item "*.vps"
    }
    if($fold -notlike "*_$today*"){$fold | Rename-Item -NewName {$fold.Name -Replace '[_]\d{6}',('_'+$today)}}
}

popd

}

PROCESS{}
END{}

File Archiver.Bat (1 line)

start powershell -File Archiver.ps1 -rootFolder "%1" -folderName1 "%2" -folderName2 "%3" -archiveFolder "%4"

Command Shell invocation:

archiver c:\scripting\archiver LIFE_ ANNUAL_ _OLD

Or for a batch file that is double click to run, just type the parameters in to the batch file like so:

start powershell -File Archiver.ps1 -rootFolder "C:\scripting\archiver" -folderName1 "ANNUAL_" -folderName2 "LIFE_" -archiveFolder "_OLD"

edit changed "-Exclude $archiveFolder" to "-File" in 2nd gci command

edit 2 - adjust for two file/folder names, also include batch file for 'double click' to run.

edit 3 swapped day/month/year for month/day/year per SimonS suggestion.

Brian

Posted 2019-02-08T18:40:35.057

Reputation: 681

Thank you for your solution! Is it possible to have several folders be processed at once? IE: The Life and the Annual? Also, I don't quite understand the last part about the Command Shell Invocation. Do I have to type that out into PowerShell each time I want to run the script? – Ovaryraptor – 2019-02-14T15:57:02.577

I will edit it to do two folders at once. To run this solution you only need to run the batch file. Make one file Archiver.Ps1, make a second file, Archiver.Bat, then type in the Command Shell invocation to run the solution. If you would prefer to open the powershell ISE to run it you can do that as well and just edit the parameters at the top of the file. – Brian – 2019-02-14T16:07:18.320

Is command shell the same as the PowerShell? – Ovaryraptor – 2019-02-14T16:11:05.217

Command shell is the old windows DOS shell run by typing or running CMD. How do you want to run the batch file? By double clicking on it? – Brian – 2019-02-14T16:14:04.147

@Ovaryraptor *fixed for two folders. Ask if you have any questions and I will try to explain better. – Brian – 2019-02-14T16:54:30.980

Double-clicking would be the way because I want to be able to just edit the path/folders and run this for other jobs. – Ovaryraptor – 2019-02-14T16:55:58.437

@Ovaryraptor Okay make a one line batch file like the one at the bottom of my post and just change your base folder name. – Brian – 2019-02-14T17:00:03.383

Let us continue this discussion in chat.

– Ovaryraptor – 2019-02-14T18:16:49.433

@Brian instead of creating two seperate parameters for the folders, you should just create one folder parameter, but make it type [string[]], then Ovaryraptor can use it for as many folders as he likes. the way you do it now, he/you would always have to edit the script if there should be more than two folders – SimonS – 2019-02-14T18:56:02.947

I just tested this script on my computer, and it did not rename any files in my folder (not even the indd), also it renamed my top folder the wrong way (day before month). also it did not remove any *.vps or *.pdf in a subdirectory of that folder. @Ovaryraptor I don't want to talk this answer down, but please consider testing my script too. – SimonS – 2019-02-14T19:06:18.370

@SimonS My script will only remove files from folders with "LIFE_" or "ANNUAL_" in their name. If a subfolder is also named similarly, then it would work. For file renaming, my script is targeting an underscore plus characters. For your folder names, if you would like the day and month reversed swap %d%m%y for %m%d%y. – Brian – 2019-02-14T19:14:54.580

and I see now that is the way he wanted the date. *fixed thank you SimonS – Brian – 2019-02-14T19:20:53.327

@Brian you're welcome. you're also missing Param( at the top by the way ;-) – SimonS – 2019-02-14T19:24:02.617

Doh! it was on the same line as my tick marks ``` – Brian – 2019-02-14T19:33:08.110

1

I also (finally) have a PowerShell solution.

You should create a PowerShell Profile and put the function below in it. it will then be ready to use whenever you start PowerShell.

Change the $Destination Path in the Param() block to whatever Backup Destination Path you'd like.

Here's some examples how to use the function:

# This will backup the specified path to the default destination specified in the function
Backup-Folder "C:\install\TestApp"

# You can also Backup multiple paths at once
Backup-Folder "C:\install\TestApp","D:\somepath\xy_020317"

# You can also overwrite the destination where the folder should be backed up to
Backup-Folder "C:\install\TestApp" -Destination "G:\MyNewFavoriteBackupLocation"

# You can also overwrite the setting for what Extensions to delete
Backup-Folder "C:\install\TestApp" -DeleteExtensions ".xlsx",".docx",".pdf"

# You can combine all of the above to be as flexible as you'd like
Backup-Folder "C:\install\TestApp","D:\somepath\xy_020317" -Destination "E:\xy" -DeleteExtensions ".ai"

btw. I from your comments I feel like, you want a batch invocation, by putting this function in your PowerShell profile, you can create a *.bat file wherever you want (e.g desktop or wherever) with the following line:

powershell "Backup-Folder 'C:\foo\folder1', 'C:\bar\ANNUAL_323232', 'E:\somewhere'"

This function will:

  • Backup the whole folder to the $Destination
  • Delete all files with a extension specified in $DeleteExtensions in the folder
  • Check if we have to change the name of the folder and it's files, otherwise does not rename
  • if the date is not the same as it was before, it will rename folder and files to the new date
  • if there was no date on a file, it will add it

Here's the fully commented function:

function Backup-Folder {

    Param(
        [ValidateScript({ Test-Path $_ })]
        [string[]]$Path,
        [string]$Destination = "C:\install\_DONE",
        [string[]]$DeleteExtensions = @(".pdf",".vps")
    )

    # Creating Destination Directory if not already present
    mkdir $Destination -ErrorAction SilentlyContinue > $null

    # Getting the Date.
    $Date = (Get-Date).ToString("MMddyy")

    # Looping over each path
    foreach ($p in $path) {

        # Copy the whole folder to the destination
        Copy-Item $p $Destination -Recurse -Force

        # Get Folder Data
        $Folder = Get-Item $p 

        # Get Old and New Name
        $Folder.Name -match '\d{6}' > $null
        $Old = $Matches.GetEnumerator() | select -expand Value
        $New = if ($Old) { $Folder.Name -replace $Old,$Date } 
               else { "{0}_{1}" -f $Folder.Name,$Date }

        # if the Old Date is not the same as the new date Rename Folders and return them 
        # else return the original folder object
        $RenamedFolder = if ($Old -ne $Date) { Rename-Item $Folder -NewName $New -PassThru }
                         else { $Folder }

        # Get all Files in subfolder and loop over them
        # Add -Recurse after $RenamedFolder if you also want the script to go through 
        # all files of all subdirectories
        Get-ChildItem $RenamedFolder | ? { !$_.PsIsContainer } | foreach {

            # if the extension should be deleted, delete it
            if ($_.Extension -in $DeleteExtensions) {
                # Delete Item
                Remove-Item $_.FullName -Force
            }
            # else rename it.
            else {
                # Get Old and New Name
                $_.BaseName -match '\d{6}' > $null
                $OldName = $Matches.GetEnumerator() | select -expand Value
                $NewName = if ($OldName) { $_.Name -replace $OldName,$Date } 
                           else { "{0}_{1}{2}" -f $_.BaseName,$Date,$_.Extension }

                # Finally Rename Item if the Old Date is not the same as the new date
                if ($OldName -ne $Date) { Rename-Item $_.FullName -NewName $NewName }
            }
        }
    }
}

We could also include a logic to be able to specify what file-extensions should be kept, additionally to be able to tell which ones should be deleted, if you see the need to do so.

SimonS

Posted 2019-02-08T18:40:35.057

Reputation: 4 566

Thank you for your solution! Is there a method where I don't have to keep editing the profile to update it to a new "_DONE" destination? Could it just look for a "_DONE" in any folder I run the .bat in? – Ovaryraptor – 2019-02-14T21:25:46.323

@Ovaryraptor you never have to update your profile, if you put this function in your profile, it just means that you can automatically use it when you start powershell. the path you set in your profile is just the default destination, you can always overwrite it by just specifying another destination like Backup-Folder C:\foo\ANNUAL_020219 -Destination D:\somewhere – SimonS – 2019-02-14T22:04:19.863

I get that I can remap the .bat to run the script I think this line is confusing me: [string]$Destination = "C:\install\_DONE", – Ovaryraptor – 2019-02-14T22:44:26.647

@Ovaryraptor this is just the default value. that means, if you run the command without the -destination parameter, it will copy it to the default destination. if you specify -destination when running the command, it will overwrite the default setting with the new value you specified. I did this because it makes the command very flexible, you only need to specify the -destination parameter if you want to backup to another destination than the default one. – SimonS – 2019-02-15T08:20:07.193

Ahh gotcha thanks for clearing that up! When I open PowerShell or try and invoke the script through batch I get this error

– Ovaryraptor – 2019-02-15T20:26:51.707

@Ovaryraptor oh no, you're running an old PowerShell version. you could install https://www.microsoft.com/en-us/download/details.aspx?id=54616 then the script would work.

– SimonS – 2019-02-16T11:44:31.953

0

Use logrotate

This is exactly what it's for..

Config file logrotate.conf

/tmp/mydir/* {
    daily
    dateext
    olddir /tmp/myarchive
}

Then run it.

touch /tmp/mydir/mylog1.txt /tmp/mydir/myotherlog2.txt
logrotate -vf ./logrotate.conf -s mystatus.logrotate

You should see something like this in verbose mode

reading config file ./logrotate.conf
olddir is now /tmp/myarchive
Reading state from file: mystatus.logrotate
Allocating hash table for state file, size 64 entries
Creating new state
Creating new state
Creating new state
Creating new state

Handling 1 logs

rotating pattern: /tmp/mydir/*  forced from command line (no old logs will be kept)
olddir is /tmp/myarchive, empty log files are rotated, old logs are removed
considering log /tmp/mydir/mylog1.txt
  Now: 2019-02-18 00:08
  Last rotated at 2019-02-18 00:00
  log needs rotating
considering log /tmp/mydir/myotherlog2.txt
  Now: 2019-02-18 00:08
  Last rotated at 2019-02-18 00:00
  log needs rotating
rotating log /tmp/mydir/mylog1.txt, log->rotateCount is 0
dateext suffix '-20190218'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
glob finding old rotated logs failed
renaming /tmp/mydir/mylog1.txt to /tmp/myarchive/mylog1.txt-20190218
disposeName will be /tmp/myarchive/mylog1.txt-20190218
removing old log /tmp/myarchive/mylog1.txt-20190218
rotating log /tmp/mydir/myotherlog2.txt, log->rotateCount is 0
dateext suffix '-20190218'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
glob finding old rotated logs failed
renaming /tmp/mydir/myotherlog2.txt to /tmp/myarchive/myotherlog2.txt-20190218
disposeName will be /tmp/myarchive/myotherlog2.txt-20190218
removing old log /tmp/myarchive/myotherlog2.txt-20190218

logrotate is already on all Linux systems. It likely already runs on your system (see /etc/logrotate.conf or /etc/logrotate.conf.d/) and you can install it on Windows with Logwot8

Evan Carroll

Posted 2019-02-08T18:40:35.057

Reputation: 1