Created
November 3, 2013 19:52
-
-
Save YuriyGuts/7294085 to your computer and use it in GitHub Desktop.
A PowerShell script that dumps the entire file tree of the specified folder(s) and saves the results to a ZIP archive.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Dumps the entire file tree of the specified folder(s) and saves the results to a ZIP archive. | |
.PARAMETER SourceFolderList | |
Path to the folder(s) to retrieve the file tree for. Multiple folders should be separated by the pipe character ("|"). | |
.PARAMETER BackupStorageList | |
Path to the folder(s) where the ZIP file will be stored. Multiple folders should be separated by the pipe character ("|"). | |
.PARAMETER BackupName | |
Name of the backup (will be included in the ZIP file name). | |
.PARAMETER CombineIntoSingleArchive | |
Optional switch. When used with multiple source folders, specifies that the output files should be put in a single archive instead of separate ones. | |
.PARAMETER SuppressAutogeneratedName | |
Optional switch. If specified, auto-generated parts will not be appended to the backup file name. | |
.EXAMPLE | |
Dump-FileTree -SourceFolder "D:\My Projects" -BackupStorage "E:\Backup" -BackupName "Projects" | |
.EXAMPLE | |
Dump-FileTree -SourceFolderList "D:\Music|E:\Video" -BackupStorageList "E:\Backup|Y:\Backup|Z:\Backup" -BackupName "Media Library" -CombineIntoSingleArchive -SuppressAutogeneratedName | |
.EXAMPLE | |
Dump-FileTree -src "D:\Music|E:\Video" -dest "E:\Backup|Y:\Backup|Z:\Backup" -name "Media Library" -combine -suppressautoname | |
#> | |
param | |
( | |
[Parameter(Mandatory = $true, HelpMessage="Path to the folder(s) to retrieve the file tree for. Multiple folders should be separated by the pipe character (`"|`").")] | |
[Alias("src")] | |
[Alias("SourceFolder")] | |
[string] | |
$SourceFolderList, | |
[Parameter(Mandatory = $true, HelpMessage="Path to the folder(s) where the ZIP file will be stored. Multiple folders should be separated by the pipe character (`"|`").")] | |
[Alias("dest")] | |
[Alias("BackupFolder")] | |
[Alias("BackupFolderList")] | |
[Alias("BackupStorage")] | |
[string] | |
$BackupStorageList, | |
[Parameter(Mandatory = $false, HelpMessage="Name of the backup (will be included in the ZIP file name).")] | |
[Alias("name")] | |
[string] | |
$BackupName = $env:COMPUTERNAME, | |
[Parameter(Mandatory = $false, HelpMessage="Optional switch. When used with multiple source folders, specifies that the output files should be put in a single archive instead of separate ones.")] | |
[Alias("combine")] | |
[Switch] | |
$CombineIntoSingleArchive, | |
[Parameter(Mandatory = $false, HelpMessage="Optional switch. If specified, auto-generated parts will not be appended to the backup file name.")] | |
[Alias("suppressautoname")] | |
[Switch] | |
$SuppressAutoGeneratedName | |
) | |
function Dump-DirectoryTree([string]$rootPath) | |
{ | |
Get-ChildItem -Path $rootPath -Directory -Force -Recurse -ErrorAction SilentlyContinue | | |
Sort-Object FullName | | |
&{ | |
process | |
{ | |
"{0:yyyy-MM-dd HH:mm} {1}" -f $_.CreationTime, $_.FullName | |
} | |
} | |
} | |
function Dump-FileTree([string]$rootPath) | |
{ | |
Get-ChildItem -Path $rootPath -Force -Recurse -ErrorAction SilentlyContinue | | |
Sort-Object FullName | | |
&{ | |
begin | |
{ | |
$fileCount = 0 | |
$directoryCount = 0 | |
$overallSize = 0 | |
} | |
process | |
{ | |
$fileSizeColumnText = " " * 10 + "<DIR>" | |
if ($_.PsIsContainer) | |
{ | |
$directoryCount++ | |
} | |
else | |
{ | |
$fileCount++ | |
$overallSize += $_.Length | |
$fileSizeColumnText = ("{0,15:N0}" -f $_.Length) | |
} | |
"{0} {1:yyyy-MM-dd HH:mm} {2}" -f $fileSizeColumnText, $_.LastAccessTime, $_.FullName | |
} | |
end | |
{ | |
"" | |
"Total: {0:N0} files, {1:N0} directories, {2:N0} bytes" -f $fileCount, $directoryCount, $overallSize | |
"" | |
} | |
} | |
} | |
function Dump-DirectoryAndFileTree([string]$rootPath) | |
{ | |
$directoryTree = Dump-DirectoryTree -rootPath $rootPath | |
$fileTree = Dump-FileTree -rootPath $rootPath | |
$separator = "-" * 50 | |
$separator | |
"Directory tree of $rootPath" | |
$separator | |
$directoryTree | |
"" | |
$separator | |
"File tree of $rootPath" | |
$separator | |
$fileTree | |
} | |
function Sanitize-FileName([string]$fileName) | |
{ | |
$invalidChars = [IO.Path]::GetInvalidFileNameChars() + [IO.Path]::GetInvalidPathChars(); | |
[Text.StringBuilder]$stringBuilder = New-Object System.Text.StringBuilder($fileName) | |
foreach ($char in $invalidChars) | |
{ | |
$stringBuilder = $stringBuilder.Replace($char, "_") | |
} | |
$result = $stringBuilder.ToString().Trim(@(' ', '_')) | |
return $result | |
} | |
function Get-DumpFileName([string]$baseName, [bool]$useAutoGeneratedName) | |
{ | |
if ($useAutoGeneratedName) | |
{ | |
return "{0} File Tree Backup {1:yyyy-MM-dd} T{1:HHmmss}.txt" -f $baseName, (Get-Date) | |
} | |
else | |
{ | |
return $baseName | |
} | |
} | |
function Get-DumpZipFileName([string]$dumpFileName) | |
{ | |
# Replace any extension with .zip | |
$dotIndex = $dumpFileName.LastIndexOf("."); | |
if ($dotIndex -ge 0) | |
{ | |
return $dumpFileName.Substring(0, $dotIndex) + ".zip" | |
} | |
else | |
{ | |
return $dumpFileName + ".zip" | |
} | |
} | |
function Add-ZipArchive([string]$sourceFile, [string]$zipFile) | |
{ | |
if (-not (Test-Path $zipFile)) | |
{ | |
Set-Content $zipFile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18)) | |
(Get-ChildItem $zipFile).IsReadOnly = $false | |
} | |
$shellApplication = New-Object -COMObject "Shell.Application" | |
$zipPackage = $shellApplication.NameSpace($zipFile) | |
# Wait for the shell to compress the file. | |
$fileCountBefore = $shellApplication.NameSpace($zipPackage).Items().Count | |
$zipPackage.CopyHere($sourceFile) | |
$fileCountAfter = $fileCountBefore | |
while ($fileCountAfter -eq $fileCountBefore) | |
{ | |
Start-Sleep -Milliseconds 250 | |
$fileCountAfter = $shellApplication.NameSpace($zipPackage).Items().Count | |
} | |
Start-Sleep -Milliseconds 2000 | |
} | |
function Get-FixedDrives() | |
{ | |
return [IO.DriveInfo]::GetDrives() | Where { $_.DriveType -eq "Fixed" } | |
} | |
function Do-BackupJob | |
{ | |
param | |
( | |
[string]$sourceDirectory, | |
[string]$backupStorage, | |
[string]$backupName, | |
[string]$backupZipFileName, | |
[bool]$useAutoGeneratedName | |
) | |
Write-Host "Processing $sourceDirectory... " -NoNewline | |
if ([String]::IsNullOrEmpty($sourceDirectory) -or (-not (Test-Path $sourceDirectory))) | |
{ | |
$sourceDirectory = (Get-Location).Path | |
} | |
if ([String]::IsNullOrEmpty($backupStorage) -or (-not (Test-Path $backupStorage))) | |
{ | |
$backupStorage = (Get-Location).Path | |
} | |
$dumpFileName = ([IO.Path]::Combine($backupStorage, (Get-DumpFileName -baseName $backupName -useAutoGeneratedName $useAutoGeneratedName))) | |
Dump-DirectoryAndFileTree -rootPath $sourceDirectory > $dumpFileName | |
$zipFileName = $backupZipFileName | |
if ([String]::IsNullOrEmpty($backupZipFileName)) | |
{ | |
$zipFileName = Get-DumpZipFileName([string]$dumpFileName) | |
} | |
Add-ZipArchive -sourceFile $dumpFileName -zipFile $zipFileName | |
Remove-Item $dumpFileName | |
Write-Host "Done" | |
} | |
# ---------- Entry point ---------- | |
Write-Host "File tree dump in progress" | |
$startTime = Get-Date | |
$sourceFolders = $SourceFolderList.Split("|") | |
$backupLocations = $BackupStorageList.Split("|") | |
$resultZipFiles = @() | |
$backupZipFileName = $null | |
if ($CombineIntoSingleArchive) | |
{ | |
$backupZipFileName = (Get-DumpZipFileName ([IO.Path]::Combine($backupLocations[0], (Get-DumpFileName -baseName (Sanitize-FileName $backupName) -useAutoGeneratedName (-not $SuppressAutoGeneratedName))))) | |
$resultZipFiles += $backupZipFileName | |
} | |
foreach ($sourceDir in $sourceFolders) | |
{ | |
if (-not $CombineIntoSingleArchive) | |
{ | |
if ($sourceFolders.Length -gt 1) | |
{ | |
$baseName = $BackupName + " " + $sourceDir | |
} | |
else | |
{ | |
$baseName = $BackupName | |
} | |
$backupZipFileName = (Get-DumpZipFileName ([IO.Path]::Combine($backupLocations[0], (Get-DumpFileName -baseName (Sanitize-FileName $baseName) -useAutoGeneratedName (-not $SuppressAutoGeneratedName))))) | |
$resultZipFiles += $backupZipFileName | |
} | |
Do-BackupJob -sourceDirectory $sourceDir -backupStorage $backupLocations[0] -backupName (Sanitize-FileName $sourceDir) -backupZipFileName $backupZipFileName -useAutoGeneratedName (-not $SuppressAutoGeneratedName) | |
} | |
for ($i = 1; $i -lt $backupLocations.Length; $i++) | |
{ | |
foreach ($zipFile in $resultZipFiles) | |
{ | |
Copy-Item -Path $zipFile -Destination $backupLocations[$i] -Force | |
} | |
} | |
$endTime = Get-Date | |
$timeElapsed = ($endTime - $startTime).TotalSeconds | |
Write-Host ("Completed in {0:N1} seconds" -f $timeElapsed) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment