I'm trying to zip all files in all subdirectories that are older than 31 days.
The following code mainly works, but does some things wrong. It creates new folders and zips them, as desired.
But, it doesn't seem to move the old files into the newly-created directories befoee they are zipped. The line Get-ChildItem -Exclude *.zip | ? {$_.LastWriteTime -lt (Get-date).AddDays(-31) -and -not $_.psIsContainer} | Move-Item -destination $newpath should do that, but doesn't. When I try to run it on its own in the console, it works fine however.
Also, in a tree of folders, this code won't work on the outer edges of the tree. What I mean is that the last folder will not have a new folder created within it.
Does anyone know where I'm going wrong?
function zipfiles ($path,$name){
$date = "$((Get-Date).ToString('yyyy-MM-dd'))"
$newpath = "$path$date"
New-Item -ItemType Directory -Path $newpath
Get-ChildItem -Exclude *.zip | ? {$_.LastWriteTime -lt (Get-date).AddDays(-31) -and -not $_.psIsContainer} | Move-Item -destination $newpath
[Reflection.Assembly]::LoadWithPartialName( "System.IO.Compression.FileSystem" )
$src_folder = $newpath
$destfile = "$newpath.zip"
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includebasedir = $false
[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder,$destfile,$compressionLevel, $includebasedir)
Remove-Item $newpath -recurse
}
Get-ChildItem -Recurse -Directory | ForEach-Object { zipfiles($($_.FullName),$($_.Name))}
Related
I've searched through both StackOverflow and SuperUser to try to figure this out, and I'm still getting plagued by a problem I can't figure out how to fix. I know it's something simple, but after playing with it for an hour I'm still stumped. Simple question: how the heck do I tell Get-Childitem to exclude folders?
Right up front here's the code that doesn't work:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4';
Get-ChildItem -Path $sourceDir -Directory -Recurse |
where {$_.fullname -notin $excludeThese} |
Get-ChildItem -Path $sourceDir | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Move-Item -Destination $nextName -Force -Verbose -WhatIf
}
}
The underlying concept here already works:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
Get-ChildItem -Path $sourceDir -File -Recurse | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Copy-Item -Destination $nextName -Verbose
}
Basically what this does is it moves folders from one place to another, and if files exist in both places, it renames files coming in. It helps keep my archive drive clear. But there are three folders there that I want to exclude because I still pull assets from them regularly, so I don't need those files moved.
Hence the difference between the two code samples: in the first one, I'm trying to get Get-Childitem to exclude a specific trio of folders, while this second one just grabs everything all at once.
I tried just doing a straight -Exclude with $excludeThese as the variable, without success; I tried skipping the variable approach altogether and just putting the folder names in after -Exclude. Still didn't work. I also tried putting in the entire path to the folders I wanted to exclude. No good--no matter what I did, the -WhatIf showed that the script was trying to move everything, including the folders I was theoretically excluding.
The last trick I tried was one I came across here on SO, and that was to go a gci with the exclude argument first, then do another gci after it. That still failed, so now I have to turn to the experts for help.
I would use a regex string created from the (escaped) directory names to exclude to make sure files withing these folders are ignored.
Also, by using a lookup Hashtable of all file names already present in the target folder, figuring out if a file with a certain name already exists is extremely fast.
$sourceDir = 'E:\Deep Storage'
$targetDir = 'W:\Deep Storage'
$excludeThese = 'Projects2','Projects3','Projects4';
# create a regex string with all folder names to exclude combined with regex OR (|)
$excludeDirs = ($excludeThese | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# create a lookup Hashtable and store the filenames already present in the destination folder
$existingFiles = #{}
Get-ChildItem -Path $targetDir -File | ForEach-Object { $existingFiles[$_.Name] = $true }
Get-ChildItem -Path $sourceDir -File -Recurse |
Where-Object {$_.DirectoryName -notmatch $excludeDirs} |
ForEach-Object {
# construct the new filename by appending an index number if need be
$newName = $_.Name
$count = 1
while ($existingFiles.ContainsKey($newName)) {
$newName = "{0}_{1}{2}" -f $_.BaseName, $count++, $_.Extension
}
# add this new name to the Hashtable so it exists in the next run
$existingFiles[$newName] = $true
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $targetDir -ChildPath $newName
$_ | Move-Item -Destination $newFile -Force -Verbose -WhatIf
}
Assuming the excluded directories are at the top:
$sourceDir="E:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4'
get-childitem $sourcedir -exclude $excludethese | get-childitem -recurse
I'm pretty new to Powershell in general, I've come across some partial solutions, but I can't find the right fit.
Ideally I would like to Archive (Zip) with Powershell but only files of a certain age (x days/months), but I would prefer keeping the folder structure and also add exclusions.
Here's what I've been trying to use (without success):
$inputFolder = "C:\Temp\Test"
$excludeFolders = #("\subfolderToKeep")
$ouputFileName="C:\Temp\archive.zip"
$Daysback = "-5"
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays($Daysback)
$tempFolder = [System.IO.Path]::GetTempFileName()
Remove-Item $tempFolder -Force
New-Item -Type Directory -Path $tempFolder -Force
$exclude =#()
$excludeFolders | ForEach-Object {
$exclude+=(Join-Path $inputFolder $_)
Get-ChildItem (Join-Path $inputFolder $_) -Recurse |
ForEach-Object{$exclude+=$_.FullName}}
Get-ChildItem $inputFolder -Recurse | Where-Object { $_.FullName -notin $exclude -and $_.LastWriteTime -lt $DatetoDelete } |
Copy-Item -Destination {Join-Path $tempFolder $_.FullName.Substring($inputFolder.length)}
Get-ChildItem $tempFolder |
Compress-Archive -DestinationPath $ouputFileName -Update
The current setup would work without the date filtering:
-and $_.LastWriteTime -lt $DatetoDelete
I am working on some PowerShell script that automatically moves folders and files from the 'X-Drive' folder and move it into the 'Old' folder which also is inside the 'X-Drive' folder, but I want it to keep the first layer folders only, all what's inside can be moved but only the folder needs to be kept, but it also needs to be in the 'Old' folder.
$exclude = #('Keep 1', 'Keep 2')
Get-ChildItem -Path "C:\X-Drive" -Recurse -Exclude $exclude |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-0) } |
Move-Item -Destination "C:\X-Drive\Old" -ErrorAction 'SilentlyContinue'
Enumerate the subfolders of C:\X-D_rive, then move their content to corresponding subfolders in C:\X-Drive\old, e.g. like this:
$refdate = (Get-Date).AddDays(-1)
Get-ChildItem 'C:\X-Drive' -Directory -Exclude $exclude | ForEach-Object {
$dst = Join-Path 'C:\X-Drive\old' $_.Name
If (-not (Test-Path -LiteralPath $dst)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Get-ChildItem $_.FullName -Recurse | Where-Object {
$_.LastWriteTime -lt $refdate
} | Move-Item -Destination $dst
}
You may want to add old to $excludes, BTW.
The code assumes you're running PowerShell v3 or newer.
There is a directory where I am moving folders, from a different directory without any logic(randomly anytime) and these folders needs to be deleted after 10 days of their moving here.
So, will this work? -
$limit = (Get-Date).AddDays(-10)
$path = "C:\Some\Path"
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
What I don't know is that this creation time parameter would be updated when I move it to the new directory???
As requested in comments, a solution with renaming folders:
Moving a folder:
$sOldPath = "C:\oldpath\foldertomove" # Change to your actual path
$sNewPath = "C:\newpath" # Change to your actual new path
$sDate = Get-Date -Format "yyyMMdd"
$oFolderToMove = Get-Item -Path $sOldPath
Move-Item -Path $sOldPath -Destination $sNewPath
Rename-Item -Path (Join-Path -Path $sNewPath -ChildPath $oFolderToMove.Name) `
-NewName ("{0}-{1}" -f $sDate, $oFolderToMove.Name)
Resulting path: C:\newpath\yyyyMMdd-foldertomove
Deleting old folders:
$sNewPath = "C:\newpath" # Change to your actual new path
$sDateLimit = ((Get-Date).AddDays(-10)).ToString("yyyyMMdd")
# Assuming that all folders in $sNewPath have date-prefixed names.
Get-ChildItem -Path $sNewPath |
Where-Object { $_.PSIsContainer } |
Foreach-Object {
$sDate = [UInt32]($_.Name.Substring(0,8))
if ($sDate -lt $sDateLimit) {
# Deletes folder and everything in it. Remove -WhatIf switch to execute.
Remove-Item -Path $_.FullName -Recurse -Force -WhatIf
}
}
I'm trying to move all items except a certain type of file. In this case *.msg. It does fine if the excluded file resides within the parent folder. However, the moment that same type of file is located within a subdirectory, it fails to leave the file in place and instead moves it to the new location.
username = Get-Content '.\users.txt'
foreach ($un in $username)
{
$destA = "c:\users\$un\redirectedfolders\mydocuments"
$destB = "c:\users\$un\redirectedfolders\desktop"
$sourceA = "C:\users\$un\mydocuments"
$sourceB = "C:\users\$un\desktop"
New-Item -ItemType Directory -Path $destA, $destB
Get-ChildItem $sourceA -Exclude '*.msg' -Recurse | Move-Item -Destination {Join-Path $destA $_.FullName.Substring($sourceA.length)}
Get-ChildItem $sourceB -Exclude '*.msg' -Recurse | Move-Item -Destination {Join-Path $destB $_.FullName.Substring($sourceB.length)}
}
This is due to the filtering done by the Get-ChildItem exclude filter. It's kind of a known issue, and if you really want I could probably dig up some reference documentation, but it may take some time. Regardless, GCI doesn't handle wildcards very well when it comes to filtering things. What you are probably better off doing is piping it to a Where command like this:
$username = Get-Content '.\users.txt'
foreach ($un in $username)
{
$destA = "c:\users\$un\redirectedfolders\documents"
$destB = "c:\users\$un\redirectedfolders\desktop"
$sourceA = "C:\users\$un\documents"
$sourceB = "C:\users\$un\desktop"
New-Item -ItemType Directory -Path $destA, $destB
GCI $sourceA -recurse | ?{$_.Extension -ne ".msg" -and !$_.PSIsContainer} | %{
$CurDest = Join-Path $destA $_.FullName.Substring($sourceA.length)
If(!(Test-Path $CurDest.SubString(0,$CurDest.LastIndexOf("\")))){New-Item -Path $CurDest -ItemType Directory|Out-Null}
$_ | Move-Item -Destination $CurDest
}
GCI $sourceB -recurse | ?{$_.Extension -ne ".msg" -and !$_.PSIsContainer} | %{
$CurDest = Join-Path $destB $_.FullName.Substring($sourceB.length)
If(!(Test-Path $CurDest.SubString(0,$CurDest.LastIndexOf("\")))){New-Item -Path $CurDest -ItemType Directory|Out-Null}
$_ | Move-Item -Destination $CurDest
}
}
Edit: Ok, now excludes folders, and also keeps folder structure.
Edit2: Re-designed to do a ForEach loop on the files, build the destination path as $CurDest, test to make sure it exists and make it if it doesn't, then move the files. Also changed mydocuments to documents which is the path to a user's My Documents folder.