Hi there,
I have a PS script which does a great job of finding log files more than 7 days old and spitting them out to another disk as zip files. The only problem is it zips all of the log files older than 7 days as individual files rather than what I want, which is for it to find files older than 7 days and zip them in a single zip file with the parent folder name.
For example in C:\inetpub\logs\LogFiles I have the following folder structure:
C:\inetpub\logs\LogFiles\W3SVC1\logs.txt,logs2.txt etc
C:\inetpub\logs\LogFiles\W3SVC2\logs.txt,logs2.txt etc
Both contain lots of txt log files.
I want to look at these txt files, zip those older than 7 days, then output a single zip file for each W3SVC folder named the same as each W3SVC folder containing the log files older than 7 days from the original W3SVC folder.
This is the script I have so far but I’m not sure how to change the logic so that it does the above:
#usage: new-zip c:\demo\myzip.zip
function New-Zip
{
param([string]$zipfilename)
set-content $zipfilename (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
(dir $zipfilename).IsReadOnly = $false
}
#usage: dir c:\demo\files*.* -Recurse | add-Zip c:\demo\myzip.zip
function Add-Zip
{
param([string]$zipfilename)
if(-not (test-path($zipfilename)))
{
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
(dir $zipfilename).IsReadOnly = $false
}
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipfilename)
foreach($file in $input)
{
$zipPackage.CopyHere($file.FullName)
Start-sleep -milliseconds 500
}
}
$targetFolder = 'C:\inetpub\logs\Test'
$destinationFolder = 'D:\TestLogZip'
$now = Get-Date
$days = 7
$lastWrite = $now.AddDays(-$days)
Get-ChildItem $targetFolder -Recurse | Where-Object { $_ -is [System.IO.FileInfo] } | ForEach-Object {
If ($.LastWriteTime -lt $lastWrite)
{
$ | New-Zip $($destinationFolder + $.BaseName + “.zip”)
$ | Add-Zip $($destinationFolder + $_.BaseName + “.zip”)
}
}
Any help you can give would be greatly appreciated
Thanks
Sam
There is a Compress-Archive cmdlet in newer versions of Powershell
Unfortunately it do not support folders in archive for individual files, so
I can suggest yoyu to move files which need to be archived in separate folder (with parent folder structure) and compress it into zip by any of available methods
The alternatives also can be direct .net usage of System.IO.Compression.ZipArchive
or Ionic.Zip.ZipFile (I prefer GitHub - haf/DotNetZip.Semverd: A fork of the DotNetZip project without signing with a solution that compiles cleanly. This project aims to follow semver to avoid versioning conflicts. DotNetZip is a FAST, FREE class library and toolset for manipulating zip files. Use VB, C# or any .NET language to easily create, extract, or update zip files.)
You can use .Net system.io.compression.filesystem to create a zip file from a directory, which keeps the structure intact.
#Copy all or move all of your logs to a temporary folder IE: c:\temp\ziptheselogs\
Add-Type -Assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory("c:\temp\ziptheselogs","c:\archive\archivedlogs.zip")
#delete your temporary folder
Looking at this compression - Zipping only files using powershell - Server Fault
and this Delete files older than 15 days using PowerShell - Stack Overflow
I came up with this. This should zip all files in a folder older than 7 days and then remove them.
$limit = (get-date).AddDays(-7)
$srcdir = “c:\test”
$zipFilename = “test.zip”
$zipFilepath = "c:\temp"
$zipFile = “$zipFilepath$zipFilename”
#Prepare zip file
if(-not (test-path($zipFile))) {
set-content $zipFile (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
(dir $zipFile).IsReadOnly = $false
}
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipFile)
$files = Get-ChildItem -Path $srcdir | where{! $.PSIsContainer -and $.CreationTime -lt $limit}
foreach($file in $files) {
$zipPackage.CopyHere($file.FullName)
#using this method, sometimes files can be ‘skipped’
#this ‘while’ loop checks each file is added before moving to the next
while($zipPackage.Items().Item($file.name) -eq $null){
Start-sleep -seconds 1
}
}
Get-ChildItem -Path $srcdir -Recurse -Force | Where-Object { !$.PSIsContainer -and $.CreationTime -lt $limit } | Remove-Item -Force
Thanks Curtis that works on a basic level but doesn’t have the logic to find files older than 7 days or name the destination directory the name of the parent dir.
Thanks Simon that’s so very close to what I need but it doesn’t retain the parent folder name and create a zip with that name. I’m currently toying between your version and mine to see if I can cobble them together and use the parent folder name for the resulting zip filename. Much appreciated input!
Ah, no. The resulting zip file is empty when I use this:
$limit = (get-date).AddDays(-7)
$srcdir = "C:\inetpub\logs\Test"
$zipFilename = “test.zip”
$zipFilepath = "D:\TestLogZip"
$zipFile = “$zipFilepath$zipFilename”
#Prepare zip file
if(-not (test-path($zipFile))) {
set-content $zipFile (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
(dir $zipFile).IsReadOnly = $false
}
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipFile)
$files = Get-ChildItem -Path $srcdir | where{! $.PSIsContainer -and $.CreationTime -lt $limit}
foreach($file in $files) {
$zipPackage.CopyHere($file.FullName)
#using this method, sometimes files can be ‘skipped’
#this ‘while’ loop checks each file is added before moving to the next
while($zipPackage.Items().Item($file.name) -eq $null){
Start-sleep -seconds 1
}
}
Get-ChildItem -Path $srcdir -Recurse -Force | Where-Object { !$.PSIsContainer -and $.CreationTime -lt $limit } | Remove-Item -Force
you have
$srcdir = "C:\inetpub\logs\Test"
try
$srcdir = “C:\inetpub\logs\Test”
I will look at getting the foldername as the zip name
screenshot
Sadly that’s not working either
ok try this. It should create a zip file with the source directory name and today’s date as its name
$a = Get-Date -format “ddMMyyyy”
$limit = (get-date).AddDays(-7)
$srcdir = “c:\test”
$name=[System.IO.Path]::GetFileName($srcdir)
$zipFilename = $name + $a + “.zip”
$zipFilepath = "c:\temp"
$zipFile = “$zipFilepath$zipFilename”
#Prepare zip file
if(-not (test-path($zipFile))) {
set-content $zipFile (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
(dir $zipFile).IsReadOnly = $false
}
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipFile)
$files = Get-ChildItem -Path $srcdir | where{! $.PSIsContainer -and $.CreationTime -lt $limit}
foreach($file in $files) {
$zipPackage.CopyHere($file.FullName)
#using this method, sometimes files can be ‘skipped’
#this ‘while’ loop checks each file is added before moving to the next
while($zipPackage.Items().Item($file.name) -eq $null){
Start-sleep -seconds 1
}
}
Get-ChildItem -Path $srcdir -Recurse -Force | Where-Object { !$.PSIsContainer -and $.CreationTime -lt $limit } | Remove-Item -Force
Thanks, it still creates a corrupt zip unfortunately. I’ve tried adding/removing the slashes at the ends of both srcdir and zipFilepath but that doesn’t seem to help
strange works fine on my machine. I have created and restored the resulting zip several times. Do you get any errors ?
Just in case it make any difference I am running on Windows 7 (I know…)
PS M:> $PSVersionTable.PSVersion
Major Minor Build Revision
5 0 10586 117
I end up with a zip files called test25082017.zip that I can open and restore all of the content
That’s odd. I’m using this on Windows Server 2012:
PS C:\windows\system32> $PSVersionTable.PSVersion
Major Minor Build Revision
4 0 -1 -1
Just a thought try are using $srcdir = “C:\inetpub\logs\Test” do you have permission to that dir? Try moving the contents to a test or temp dir and pointing the script at that
Also I ran it from the ISE
Thanks, it was something I hadn’t thought about but still get the same result despite moving srcdir to a dir I have full control over
MMM I am at a bit of a loss then as it works fine here what exactly do you end up with ? i.e. an empty zip file ? Another idea is to comment out the delete part and change $limit = (get-date).AddDays(-7) to $limit = (get-date) so it sees today’s files, as it maybe some strange time stamp thing on your files.
The only way I can reproduce the error you get is if $files is empty i.e. there are not any files newer than 7 days in the directory. After I ran the script again in the window of the ISE I typed $files and it listed the files newer than 7 days and $files.cont told me how many.
PS M:> $files
Directory: C:\test
Mode LastWriteTime Length Name
01/08/2017 15:12 434 01-12-2017results.csv
14/08/2017 15:40 664 14-40-2017results.csv
08/08/2017 13:15 194 1lines.csv
08/08/2017 12:53 194 2lines.csv
16/08/2017 08:29 441 basetext.txt
16/08/2017 08:32 1520 converted.xml
04/08/2017 09:59 10 decripted.txt
17/08/2017 09:43 4981040 help.txt
14/08/2017 15:40 387385 MSRC_CVEs2017-Jul.csv
14/08/2017 15:40 1618222 MSRC_CVEs2017-Jul.html
10/08/2017 12:15 66 name.csv
02/08/2017 14:39 16856 power.txt
04/08/2017 09:57 62 secure.txt
17/08/2017 12:33 10 servernames.txt
10/08/2017 12:16 20 test2.csv
14/08/2017 13:01 10782 testupdates.xlsx
04/08/2017 10:35 18 tocopy.txt
08/08/2017 13:25 214 withdate.csv
PS M:> $files.count
18
PS M:>
If I then run it again (without restoring the files I get this when testing $files
PS M:> C:\Powershell Scripts\folercleanup.ps1
PS M:> $files
PS M:> $files.count
0
PS M:>
Which results in an empty zip file like you have. Can you test that $files has something in it ?
Screenshot
Yes it does contain files in the last 7 days.
This is weird, must be a difference between the PS versions and possibly OS?
Thanks again for your help!
Maybe someone else here has an idea as I am out of them and don’t have a 2012 server to test it on. Maybe you could try it on another machine / operating system or something. Anyway good luck and let me know how you get on
Based on your original post, you already have that logic.
“I have a PS script which does a great job of finding log files more than 7 days old and spitting them out to another disk”