Continuing on from the last part where I defined the Get-FolderSize function the next function to be defined is Get-FolderInformation. This gets the relevant information about all the folders that could be processed and outputs that information as objects. The full script can be found here.
Get-FolderInformation
function Get-FolderInformation { param ( [Parameter(Mandatory=$True)] [string]$SourcePath, [Parameter(Mandatory=$True)] [string]$ArchivePath, [Parameter(ValueFromPipeline=$true)] [string]$Path ) Begin { Write-Debug "Entering Get-FolderInformation function" Write-Debug "Parameter : SourcePath = $SourcePath" Write-Debug "Parameter : ArchivePath = $ArchivePath" } Process { Write-Debug "Parameter : Path = $Path" if (($Path -eq $Null) -or ($Path -eq "")) { #If there is no Path specified, get everything from the Source folder. $WorkingPath=$SourcePath+"\*" }elseif (Test-Path -Path $Path) { #If Path is a valid full path, then use that $WorkingPath=$Path }else { #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath. $WorkingPath=$SourcePath+"\*"+$Path+"*" } Write-Debug "WorkingPath = $WorkingPath" $WorkingFolderList=Get-Item $WorkingPath | Where-Object -FilterScript {$_.PSISContainer} ForEach ($ChildItem in $WorkingFolderList) { Write-Debug $ChildItem.FullName #Build a hash table with all the properties we're interested in. $Properties=@{ 'Name'=$ChildItem.Name; 'FullSourcePath'=$ChildItem.FullName; 'FullArchivePath'=$ArchivePath+"\"+$ChildItem.Name 'Size'=0; 'Direction'="To Archive"; 'Moving'=$False 'Message'=""; 'Successful'=$False } #Test if it's a folder or already a symbolic link; use this to determine if we're going to or from the archive. if ($ChildItem.Attributes -like "*ReparsePoint*") { $Properties.Size=Get-FolderSize -Path $Properties.FullArchivePath #If it's a symbolic link see if it's target is in the archive folder; if it's going somewhere else #we ignore it. if (!(Test-Path $Properties.FullArchivePath)) { return } $Properties.Direction="From Archive" }else { $Properties.Size=Get-FolderSize -Path $ChildItem.FullName } #Build and output a PSObject from the hash table $Object=New-Object -TypeName PSOBject -Property $Properties Write-Output $Object } } }
Quite a chunk of code! But it’s (mostly) all important.
function Get-FolderInformation { param ( [Parameter(Mandatory=$True)] [string]$SourcePath, [Parameter(Mandatory=$True)] [string]$ArchivePath, [Parameter(ValueFromPipeline=$true)] [string]$Path ) Begin { Write-Debug "Entering Get-FolderInformation function" Write-Debug "Parameter : SourcePath = $SourcePath" Write-Debug "Parameter : ArchivePath = $ArchivePath" } Process {
The first part of the code just defines the function and the parameters we’re interested in. It needs to know the path where to look for folders to archive ($SourcePath), the path where folders are archived ($ArchivePath) and the relative or absolute path of the folder to be processed ($Path). We want to be able to stream this last parameter from the pipeline so we can process multiple paths. Note that while $SourcePath and $ArchivePath are mandatory, $Path isn’t. When $Path isn’t passed we want the script to get all the folders in $SourcePath.
As it will accept pipeline input we’ll need Begin and Process statements and we’ll put the first Write-Debug statements in the Begin block as these show the parameters that won’t change (as they’re not passed from the pipeline).
Write-Debug "Parameter : Path = $Path" if (($Path -eq $Null) -or ($Path -eq "")) { #If there is no Path specified, get everything from the Source folder. $WorkingPath=$SourcePath+"\*" }elseif (Test-Path -Path $Path) { #If Path is a valid full path, then use that $WorkingPath=$Path }else { #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath. $WorkingPath=$SourcePath+"\*"+$Path+"*" } if (!(Test-Path -Path $WorkingPath -PathType Container)) { Throw ("Path ($WorkingPath) does not exist or is not a folder.") } Write-Debug "WorkingPath = $WorkingPath"
We want to build a path to the folder or folders we want to display as options to the user for archiving (or restoring). This needs to be built from $Path but $Path can be in a range of formats;
- Empty (which we want to behave like a “*” wildcard and return everything).
- A full, explicit path to a folder to process (like “c:\games\Quake”).
- A partial or relative path (so return every folder in $SourcePath that contains “Sam”).
The code above builds $WorkingPath depending on the format of $Path.
if (($Path -eq $Null) -or ($Path -eq "")) { #If there is no Path specified, get everything from the Source folder. $WorkingPath=$SourcePath+"\*"
Here is the case where $Path is empty or $Null. $WorkingPath in this case is the all folders under the $SourcePath.
}elseif (Test-Path -Path $Path) { #If Path is a valid full path, then use that $WorkingPath=$Path
If it’s not empty or $Null we use Test-Path to see if it’s a valid path. If it is, we assume that’s what we want to process and we make $WorkingPath the same as $Path.
}else { #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath. $WorkingPath=$SourcePath+"\*"+$Path+"*" }
The default position (if $Path isn’t empty/$Null and not a valid path itself) is to assume it’s a relative path or partial filename. We then build $WorkingPath from $SourcePath and $Path (surrounded by “*” wildcards). This will match all files or folders under $SourcePath with $Path at the end of the pathname.
Write-Debug "WorkingPath = $WorkingPath" $WorkingFolderList=Get-Item $WorkingPath | Where-Object -FilterScript {$_.PSISContainer} ForEach ($ChildItem in $WorkingFolderList) { Write-Debug $ChildItem.FullName #Build a hash table with all the properties we're interested in. $Properties=@{ 'Name'=$ChildItem.Name; 'FullSourcePath'=$ChildItem.FullName; 'FullArchivePath'=$ArchivePath+"\"+$ChildItem.Name 'Size'=0; 'Direction'="To Archive"; 'Moving'=$False 'Message'=""; 'Successful'=$False }
First we build an array of all the folders in $WorkingPath. We’re not interested in files so we we filter to only return folders (Where-Object -FilterScript {$_.PSISContainer}).
For each item in the list of folders to be processed we want to create an object with all the details we’re interested in. In the code above the $Properties hash-table is created and populated with the relevant information. Later on we can use that to create an PS-Object.
Most of the properties on the hash-table are self-explanatory but a couple are in place for later use;
Direction. Used to specify if the folder is being archived or returned from archive.
Moving. If the user wants to process this folder. Defaults to $False so the user has to explicitly ask for the folder to be processed.
Message. A descriptive error message if it’s required.
Successful. Whether the action on the folder succeeded or not.
#Test if it's a folder or already a symbolic link; use this to determine if we're going to or from the archive. if ($ChildItem.Attributes -like "*ReparsePoint*") { $Properties.Size=Get-FolderSize -Path $Properties.FullArchivePath #If it's a symbolic link see if it's target is in the archive folder; if it's going somewhere else #we ignore it. if (!(Test-Path $Properties.FullArchivePath)) { return } $Properties.Direction="From Archive" }else { $Properties.Size=Get-FolderSize -Path $ChildItem.FullName } #Build and output a PSObject from the hash table $Object=New-Object -TypeName PSOBject -Property $Properties Write-Output $Object } } }
For each folder in the source path we need to check if it’s a folder to be archived or a symbolic link to a folder to return from the archive. We do that with the “if ($ChildItem.Attributes -like “*ReparsePoint*”)” line.
If the item we’re processing is a symbolic link we then make sure it’s linking to a folder in our archive path. If it’s linked somewhere else we’ll just return out of the function as it’s non-standard. Additionally we now know the direction for the process is “From Archive” so we can set that property correctly.
The Size property is set here as well; either from the size of the folder in the source path or the size of the folder in the archive path (depending on whether the item we’re processing is a symbolic link).
Finally we convert the $Properties hash-table into a PSOBject and output it.
In the next part we’ll look at the Move-ArchiveObject function, where the actual work is done.