Powershell script to backup folders older than 7 days - powershell

I have a folder X:/EmpInfo that contains many different folders. Each of these folders contains about 5-10 files.
I need to backup folders that have a modified date newer than 7 days ago. That is, modified would mean new files were added to the folder or an existing file was modified.
Example: If I run it today (11/08), it'll backup all folders in X:/EmpInfo with a modification date of 11/01 to current time today (when the script is run). It should move the entire folder, not just the modified files. Overwrite any existing folders.

It might not be "something for real like Perl" but PowerShell can handle that pretty easily. This should get you started:
$newFolders = dir X:\EmpInfo | ? {$_.PSIsContainer} | ? {$_.LastWriteTime -gt (Get-Date).AddDays(-7)}
$newFolders | % { copy $_.FullName c:\temp\archive -Recurse -Force}
Have fun with the pipeline!

My answer is a combination of the answers of MrKWatkins and Eric Nicholson. In your question you clarify that what you actually want is to copy directories where new files were added or existing files were copied. The behaviour of the last modification date for the containing directory will be different on different filesystems:
NTFS: On an NTFS file system, the modified date of a folder DOES change if the contents of the folder change.
FAT: On a FAT file system, the modified date of a folder does not change if the contents of the folder change.
Description of NTFS date and time stamps for files and folders
Therefore ideally we should first test the filesystem type of the source directory before deciding on how to determine what directories to copy:
function Copy-ModifiedSubdirectory {
param ($sourcefolder, $destinationfolder, [DateTime] $modifieddate)
# Escaping source folder for use in wmi query
$escapedsourcefolder = $sourcefolder -replace "\\","\\"
# Determine what filesystem our folder is on
$FSName = (get-wmiobject -query "select FSName from CIM_Directory where name = '$escapedsourcefolder'").FSName
if ($FSName -eq "NTFS") # The Eric Nicholson way
{
$FoldersToCopy = get-childitem $sourcefolder | where {$_.PSIsContainer} | where {$_.LastWriteTime -ge $modifieddate}
}
elseif ($FSName -eq "FAT32") # The MrKWatkins way
{
$FoldersToCopy = get-childitem $sourcefolder | where {$_.PSIsContainer} | ? { Get-ChildItem $($_.fullname) -Recurse | ? { $_.LastWriteTime -ge $modifieddate } }
}
else
{
Write-Error "Unable to Copy: File System of $sourcefolder is unknown"
}
# Actual copy
$FoldersToCopy | % { copy $_.FullName $destinationfolder -Recurse -Force}
}
To use the function:
$sevendaysago = ((get-date).adddays(-7))
copy-modifiedsubdirectory X:\EmpInfo Y:\Archive $sevendaysago

Here is a rough and ready script to get you started. I'm sure there are better ways to a lot of this...
$sevenDaysAgo = (Get-Date).AddDays(-7);
# Get the directories in X:\EmpInfo.
$directories = Get-ChildItem . | Where-Object { $_.PSIsContainer };
# Loop through the directories.
foreach($directory in $directories)
{
# Check in the directory for a file within the last seven days.
if (Get-ChildItem .\UnitTests -Recurse | Where-Object { $_.LastWriteTime -ge $sevenDaysAgo })
{
$directory
}
}

Related

Using powershell to count files but exclude subfolder in count and OCR extension

I have a directory like so: \OCRELEASE\Images$. The directory contains about 200 subfolders and each one of those subfolders also contains subfolders. So i would have:
\OCRELEASE\Images$\Image1\0-200
\OCRELEASE\Images$\Image2\0-200
\OCRELEASE\Images$\Image3\0-200
in each of the 0-200, it can contain up to 200 files. Majority of the files have no extension and labeled like 1, 2, 3, etc. I am trying to get a TOTAL count of how many files exist in \OCRELEASE\Images$\Image1 in all of its subfolders. I want to EXCLUDE any files that has a .OCR extension and I only want a count of the files with no extension (do not include the folder in the actual count, only its files). I have the following:
$FOLDER_ROOT = "\\mchocrelease\images$\images21"
$OUTPUT_LOCATION = "C:\imnet\OUT.txt"
Function DirX($directory)
{
Remove-Item $OUTPUT_LOCATION
foreach ($singleDirectory in (Get-ChildItem $directory -Recurse -Directory))
{
$count = Get-ChildItem $singleDirectory.Name -File | Measure-Object | %{$_.Count}
$summary = $singleDirectory.FullName+" "+$count+" "+$singleDirectory.LastAccessTime
Add-Content $OUTPUT_LOCATION $summary
}
}
DirX($FOLDER_ROOT)
The output looks great, but it breaks down each individual subfolder and its counts. I just want a count of all the files (exlcuding OCR) in:
\OCRELEASE\Images$\Image1
Any help appreciated
If you just want the total count of all files with no extensions that exist under \\mchocrelease\images$\images1 then this would satisfy your request.
Get-ChildItem -Path \\mchocrelease\images$\images1 -File -Recurse | where {$_.Extension -eq ''} | measure | select -ExpandProperty count
Based on your sample script it seems you want more than that. Please modify your post if so.

How can i loop through 30 folders and create 4 subfolders in each by taking part of the name of the parent folder?

So I have 30 folders that all follow the same naming convention:
XXX-application name
the X's are numbers that are between 2 and 4 digits.
In the sub folders I need these folders created:
XXX-access mngt
XXX-app controls
XXX-support docs
XXX-vendor docs
I know how to code in Java but I have never used PowerShell (my mom needed help for her job) so I need some help, I did generate the parent folders with a script that takes the names from a text file.
I'd like some help with how to go about doing this, I do have a text file with the full name of each parent folder and a separate text file that just has the numbers of each
I haven't tried anything yet, this is the script I found/adapted to generate the parent folders (this works)
$folder="F:\Test\Folders"
$txtFile="F:\Test\MainFolder.txt"
$pattern="\d+.+"
get-content $txtFile | %{
if($_ -match $pattern)
{
mkdir "$folder\$_"
}
}
This took each line in the text file and made it into a folder
Here is one way to do it:
Get-ChildItem -Path '<path to parent folder>' -Directory |
ForEach-Object {
$parent = $_
if($parent.Name -match '^(?<num>\d{2,4})-.*$') {
"access mngt", "app controls", "support docs", "vendor docs" |
ForEach-Object {
New-Item -Path $parent.FullName -Name "$($Matches.num)-$_" -ItemType Directory | Out-Null
}
}
}

Powershell: Copying files from multiple folders created x days while maintaining folder structure

I am having issues maintaining the folder structure during a Powershell copy using FOREACH loops as some folders under the parent folder need the data created longer than 90 days ago whereas others 180.
$Folders_90Days = "C:\Admin\Ripley","C:\Admin\Android","C:\Admin\Bishop"
$Folders_180Days = "C:\Admin\Archer","C:\Admin\Figgis","C:\Admin\Pam"
$Dest = 'D:\Archive_Target'
FOREACH($path in $Folders_90Days){
$files = (Get-ChildItem $path | Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-90)})
ForEACH($file in $Files){
Copy-Item $file.FullName -destination $Dest -Recurse
}
#End ForEach Folders_90Days loop
}
When the copy runs, the data copied from the source folders is copied directly into the target.
So in essence the data in Ripley copies directly to Archive_Target.
How can I get it to copy with the source folder becoming the target folder?
Example:
D:\Archive_Target\Ripley
- All the while maintaining the folder structure of Source Ripley?
Ive created a function that does exactly this without xcopy.
I put it in a workflow that allows Parallel processing.
Using Regex I get the folder that is the base for the source. If the path isnt there create it, else the real meat is in the pipe.
Get all items in source, pipe to a if older then AgeInDays then copy the item and its container which allows for the file structure to keep the same.
workflow Copy-ItemBasedOnCreation{
Param(
[string[]]$Sources,
[string]$Desination,
[int]$AgeInDays
)
foreach –parallel ($Source in $Sources){
$FixedDesination = "$($Desination)$($Source -replace '.*\\')"
if(!(Test-Path -Path $FixedDesination)){
md $FixedDesination
}
Get-ChildItem $Source -Recurse | ?{(($_.CreationTime).Day -gt $AgeInDays)} | Copy-Item -Destination $FixedDesination -Recurse -Container
}
}
Copy-ItemBasedOnCreation -Sources #("C:\Users\Default\Desktop\Source\TEST1","C:\Users\Default\Desktop\Source\TEST2") -Desination "C:\Users\Default\Desktop\Destination\" -AgeInDays 90
Copy-ItemBasedOnCreation -Sources #("C:\Users\Default\Desktop\Source\TEST4","C:\Users\Default\Desktop\Source\TEST5") -Desination "C:\Users\Default\Desktop\Destination\" -AgeInDays 180

How do I select files in a folder based on part of filename and zip them in Powershell?

I'm fairly new to Powershell(using Powershell 2.0 btw) and am trying to make a script that does several things(this is my 3rd script or so). I have most things in place but the last thing remaining is to group files of different types (xml, tfw and tif) in a folder, based on the first part of the filename(first three characters) and then zip these files into several zip-files with name like the first 3 characters, either in the same location or in a new one.
Sample of folder content:
001.tif
001.tfw
001.metadata.xml
002.tif
002.tfw
002.metadata.xml
003.tif
003.tfw
003.metadata.xml
003_svel.tif
003_svel.tfw
003_svel.metadata.xml
Wanted result:
001.zip containing 001.tif, 001.tfw, 001.metadata.xml
002.zip containing 002.tif, 002.tfw, 002.metadata.xml
003.zip containing 003.tif, 003.tfw, 003.metadata.xml, 003_svel.tif,
003_svel.tfw and 003_svel.metadata.xml
I have installed 7-zip to do the zipping and am using the commandline version. I've used 7-zip local on some testfiles and got it to work, but then it was only tif-files. I have a source folder where I search for the latest created folder and then process the files in it.
This is what I have so far(Powershell 2.0):
$dir_source = "c:\Test"
$new_folder = Get-ChildItem $dir_source -Recurse |
Where { $_.PSIsContainer} |
Sort-Object LastWriteTime -Descending |
Select-Object -ExpandProperty Fullname <-First 1
Get-ChildItem $new_folder -recurse -Exclude metafile.xml |
Group-Object {$_.Name.Substring(0,3)}
This gives me a list of grouped files in the lates created folder based on the first 3 characters in the filename. It also show what files are in each group.
Like below:
Count Name Group
----- ---- -----
3 003 {C:\Test\20150708 063255_B\003.metafile.xml, C:\Test\20150708 063255_B\003.tfw, C:\Test\20150708 063255_B\003.tif}
6 004 {C:\Test\20150708 063255_B\004.metafile.xml, C:\Test\20150708 063255_B\004.tfw, C:\Test\20150708 063255_B\004.tif,C:\Test...
6 009 {C:\Test\20150708 063255_B\009.metafile.xml, C:\Test\20150708 063255_B\009.tfw, C:\Test\20150708 063255_B\009.tif,C:\Test...
Now my next step ist to take these groups and zip them. Ideally create these zip-files in a different destination directory (I believe I can change this when setting the $directory- variable in the script below.)
foreach ($group in $dataset) {
$name = $file.name
$directory = $file.DirectoryName
$zipFile = $file.Name + ".zip"
sz a -t7z "$directory\$zipfile" "$directory\$name"
This last code is causing some trouble. I either get the message:
7-Zip (A) 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18 Error:
c:\Test\Dest_test460.zip is not supported archive System error:
Incorrect function.
,or
WARNING: Cannot find 1 file 7-Zip (A) 9.20 Copyright (c) 1999-2010
Igor Pavlov 2010-11-18 Scanning \460: WARNING: The system cannot
find the file specified.
,or it starts zipping all files on my userprofile into a zip-file. Depending on changes I do to the $group-value. I believe there are one ore more basic errors in my script causing this, and this is where I'm asking for some help. It may be that I am approaching this the wrong way by first grouping the files I want and then try to zip them?
Anyone that can see my error or give me some hint to what I have to do?
Thanks for your time!
Lee Holmes New-ZipFile do the job, he has two versions one of them using the ICSharpCode.SharpZipLib.dll to compress, and the other not require it, i wrapped the 2nd one into a function:
Function New-ZipFile {
param(
## The name of the zip archive to create
$Path = $(throw "Specify a zip file name"),
## Switch to delete the zip archive if it already exists.
[Switch] $Force
)
Set-StrictMode -Version 3
## Create the Zip File
$zipName = $executionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
## Check if the file exists already. If it does, check
## for -Force - generate an error if not specified.
if(Test-Path $zipName)
{
if($Force)
{
Remove-Item $zipName -Force
}
else
{
throw "Item with specified name $zipName already exists."
}
}
## Add the DLL that helps with file compression
Add-Type -Assembly System.IO.Compression.FileSystem
try
{
## Open the Zip archive
$archive = [System.IO.Compression.ZipFile]::Open($zipName, "Create")
## Go through each file in the input, adding it to the Zip file
## specified
foreach($file in $input)
{
## Skip the current file if it is the zip file itself
if($file.FullName -eq $zipName)
{
continue
}
## Skip directories
if($file.PSIsContainer)
{
continue
}
$item = $file | Get-Item
$null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
$archive, $item.FullName, $item.Name)
}
}
finally
{
## Close the file
$archive.Dispose()
$archive = $null
}
}
To use it for example:
dir c:\folder -Recurse | New-ZipFile -Path c:\temp\folder.zip
The Source file(for the one that use the ICSharpCode): http://poshcode.org/2202
Use my Previous Answer Function New-ZipFile and use with this one:
$FolderName = "C:\temp"
$Files = dir $FolderName
$prfx = #()
foreach ($file in $files)
{
$prfx += $file.Name.Substring(0,3)
}
$prfx = $prfx | Group
foreach ($Prf in $prfx)
{
$prf = $prf.name.ToString()
dir $Files | ? {$_.Name -match "^$prf"} | New-ZipFile -Path "$foldername\$prf.zip"
}
According to your example It will output 3 zip files like you want,
it will always use the first 3 letters of the file, you can the change this in this line $prfx += $file.Name.Substring(0,3) and set it different if needed.
Good Luck
Could not get the suggested solution to work, had problems configuring ICSharpCode. I also wanted to use 7zip, since it is still under some updating regime.
Ended up copying my files to temp folders based on the filenames and then zip each folder. After that delete the tempfolders with files. Ugly code, but it does the job.
# Create folder based on filename and copy files into respective folder
Get-ChildItem $new_folder -Filter *.* | Where-Object {!$_.PSIsContainer} | Foreach-Object{
$dest = Join-Path $_.DirectoryName $_.Name.SubString(0,3)
if(!(Test-Path -Path $dest -PathType Container))
{
$null = md $dest
}
$_ | Copy-Item -Destination $dest -Force
}
# Create zip-file of each folder
dir $new_folder | Where-Object { $_.PSIsContainer } | ForEach-Object { sz a -t7z -mx9 "$dir_dest\$_.zip" $_.FullName }
# Delete temp-folders
dir $new_folder | Where-Object { $_.PSIsContainer } | Remove-Item -Recurse

Using PowerShell to move a matching set of files with same name but different extensions to two different destinations

I would like to use PowerShell to move a matching name set of files (1 job file and 1 trigger file both havening the same name just different extensions) from one directory to another. See example below.
Source directory contains job1.zip, job1.trg, job2.zip, and job2.trg. I would like to take matching job names job1.zip and job1.trg and move it to dest1folder, only if it is empty, if not move it to dest2folder. Then loop back to perform the same logic for job2.zip and job2.trg. One thing I also have to take into consideration is the Source directory may only contain job1.zip waiting for job1.trg to be transferred. I am a newbie to PowerShell and blown hours on trying to get it working with no success. Is it possible?
This is what I have so far. I get the files to move to each destination folder using IF logic, but it moves all of the files in the source directory.
$doirun = (get-childItem "d:\ftproot\pstest\").Count
$filecount = (get-childItem "d:\ftproot\ps2\").Count
if ($doirun -le 1) {exit}
$dir = get-childitem "d:\ftproot\pstest\" | Where-Object {($_.extension -eq ".zip") -or ($_.extension -eq ".trg")}
foreach ($file in $dir)
{
if ($filecount -le 2) {Move-item "d:\ftproot\pstest\$file" "d:\ftproot\ps2\"}
else {Move-item "d:\ftproot\pstest\$file" "d:\ftproot\ps3\"}
}
Not tested extensively, but I believe this should work:
$jobs = gci d:\ftproot\pstest\* -include *.zip,*.trg |
select -expand basename | sort -unique
$jobs |foreach-object {
if (test-path d:\ftproot\pstest\$_.zip -and test-path d:\ftproot\pstest\$_.trg){
if (test-path d:\ftproot\pstest\ps2\*){
move-item d:\ftproot\pstest\$_.zip d:\ftproot\pstest\ps3
move-item d:\ftproot\pstest\$_.trg d:\ftproot\pstest\ps3
}
else {
move-item d:\ftproot\pstest\$_.zip d:\ftproot\pstest\ps2
move-item d:\ftproot\pstest\$_.trg d:\ftproot\pstest\ps2
}
}