amloessb

Backup-Files-Live.ps1 (v1.1)

Oct 10th, 2012
211
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. Live File Backup Script (for PowerShell) v1.1
  3. Written by Aaron Loessberg-Zahl
  4. Last modified 10 October 2012
  5.  
  6. Copies files from a source directory (or a single source file)
  7. to a backup destination as they are changed in the source.
  8.  
  9. Run 'Get-Help .\Backup-Files-Live.ps1' to get more information.
  10.  
  11. For comments/questions/bugs, please contact
  12.  
  13. ----------------------------------------------------------------------------
  14. "THE BEER-WARE LICENSE" (Revision 2659):
  15. <[email protected]> wrote this file. As long as you retain this
  16. notice, you can do whatever you want with this stuff. If we meet some day,
  17. and you think this stuff is worth it, you can buy me a beer in return.
  18. ----------------------------------------------------------------------------
  19.  
  20. Changelog:
  21. v1.0    10-04-2012  amloessb    Re-written in PowerShell from the original, broken liveBackup.vbs
  22. v1.1    10-10-2012  amloessb    Debugged to a working state
  23. #>
  24.  
  25. <#
  26. .SYNOPSIS
  27. Performs live backup of files.
  28.  
  29. .DESCRIPTION
  30. Checks the source at a specified interval for changes and copies changed files to the destination.  Folder hierarchies are preserved.
  31. This script runs indefinitely until:
  32.   1. ^C is pressed,
  33.   2. the top-level source disappears, or
  34.   3. the script's process is otherwise ended
  35. The script will continue to run even if files or directories within the source tree disappear.
  36.  
  37. .PARAMETER Source
  38. The location of the source file or directory to be backed up.
  39.  
  40. .PARAMETER Destination
  41. The destination directory where the backup will be saved.
  42.  
  43. .PARAMETER Interval
  44. The frequency (in seconds) at which the script will check for changes in the source (default: 5).
  45.  
  46. .INPUTS
  47. None. You cannot pipe objects to this script.
  48.  
  49. .OUTPUTS
  50. None. This script does not produce any pipeable output.
  51.  
  52. .EXAMPLE
  53. .\Backup-Files-Live -Source C:\Scripts -Destination C:\ScriptsBackup -Interval 10
  54. Backs up the contents of C:\Scripts to C:\ScriptsBackup, and checks for and backs up changes every 10 seconds.
  55.  
  56. .LINK
  57. None.
  58. #>
  59.  
  60. Param(
  61.     [Parameter(Mandatory = $TRUE,
  62.                Position = 0,
  63.                HelpMessage = "The source file or directory.")]
  64.     [ValidateNotNullOrEmpty()]
  65.     [String] $Source,
  66.    
  67.     [Parameter(Mandatory = $TRUE,
  68.                Position = 1,
  69.                HelpMessage = "The destination directory.")]
  70.     [ValidateNotNullOrEmpty()]
  71.     [String] $Destination,
  72.    
  73.     [Int] $Interval = 5
  74. )
  75.  
  76. Set-PSDebug -Strict
  77. $ErrorActionPreference = "Stop"
  78.  
  79. # Request-YNAnswer [-Question] <String>
  80. # Purpose: Asks a question and waits for the user to either answer "y" or "n".
  81. #          If the user does not answer either, the script will loop until a valid answer is given.
  82. # Returns: $true if "y", $false if "n"
  83. Function Request-YNAnswer {
  84.     Param (
  85.         [Parameter(
  86.             Mandatory = $TRUE,
  87.             HelpMessage = "The question to be asked of the user"
  88.         )]
  89.         [string] $Question
  90.     )
  91.     Process {
  92.         $strAnswer = Read-Host "$Question [y/n] "
  93.         Do {
  94.             Switch ($strAnswer.ToLower()) {
  95.                 "y" {
  96.                     $BadAnswer = $False
  97.                     $Result = $True
  98.                 }
  99.                 "n" {
  100.                     $BadAnswer = $False
  101.                     $Result = $False
  102.                 }
  103.                 Default {
  104.                     $BadAnswer = $True
  105.                     $strAnswer = Read-Host "Please answer y or n "
  106.                 }
  107.             }
  108.         } While ($BadAnswer)
  109.         Return $Result
  110.     }
  111. }
  112.  
  113. # Pause [[-Message] <String>]
  114. # Purpose: Pauses execution until a key is pressed.
  115. # Returns: N/A
  116. Function Pause {
  117.     Param (
  118.         [string] $Message="Press any key to continue..."
  119.     )
  120.     Process {
  121.         Write-Host -NoNewLine $Message
  122.         $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  123.         Write-Host ""
  124.     }
  125. }
  126.  
  127. Function CrashAndBurn ($strProblem) {
  128.     $Host.UI.WriteErrorLine($strProblem)
  129.     $Host.UI.WriteErrorLine("The script will now terminate.")
  130.     Pause
  131.     Exit 1
  132. }
  133.  
  134. # Compare-DateModified -src <String> -dest <String>
  135. # Purpose: If $src is a folder, it calls its children recursively.  If $src is a file, it
  136. #          compares the modification times of $src and $dest, and writes the information to
  137. #          $strNewFileList if $dest is newer.
  138. # Returns: N/A
  139. Function Compare-DateModified {
  140.     Param (
  141.         [String] $src,
  142.         [String] $dest
  143.     )
  144.     Process {
  145.         $objSrc = Get-Item $src
  146.         If (Test-Path $dest) { # If the file/directory doesn't exist at the destination, we don't care
  147.             If ($objSrc.PSIsContainer) {
  148.                 # Recursion case
  149.                 ForEach ($obj In Get-ChildItem $objSrc) {
  150.                     Compare-DateModified -src $obj.FullName -dest (Join-Path $dest -ChildPath $obj.Name)
  151.                 }
  152.             } Else {
  153.                 # Base case
  154.                 $objDest = Get-Item $dest
  155.                 If ($objDest.LastWriteTime -gt $objSrc.LastWriteTime) {
  156.                     $Script:strNewFileList += "$($objSrc.Name) $DTab $($objSrc.LastWriteTime) $DTab $($objDest.LastWriteTime)`r`n"
  157.                 }
  158.             }
  159.         }
  160.     }
  161. }
  162.  
  163. # Confirm-Operation -src <Srting> -dest <String>
  164. # Purpose: Asks the user to confirm the backup settings
  165. # Returns: $true if the user accepts, $false if the user rejects
  166. Function Confirm-Operation {
  167.     Param (
  168.         [String] $src,
  169.         [String] $dest
  170.     )
  171.     Process {
  172.         Write-Host "The script will now back up " -NoNewline
  173.         If (!$FileMode) {
  174.             Write-Host "the contents of"
  175.         } Else {
  176.             Write-Host ""
  177.         }
  178.         Write-Host $src
  179.         Write-Host "to"
  180.         Write-Host $dest
  181.         Write-Host ""
  182.         Return (Request-YNAnswer "Is this correct?")
  183.     }
  184. }
  185.  
  186. # Write-CopyInfo -objSrc <System.IO.FileInfo>
  187. # Purpose: Prints the time, name, and size of the given file or folder
  188. # Returns: N/A
  189. Function Write-CopyInfo {
  190.     Param (
  191.         [System.IO.FileInfo] $objSrc
  192.     )
  193.     Process {
  194.         $WriteTime = $objSrc.LastWriteTime
  195.         $FileName = $objSrc.Name
  196.         $strSize = "{0:N2}K" -f ($objSrc.Length / 1024.0)
  197.         Write-Host "$WriteTime`t$FileName`t$strSize"
  198.     }
  199. }
  200.  
  201. # CompareAndCopy -src <String> -dest <String>
  202. # Purpose: If $src is a folder, it calls its children recursively and creates directories
  203. #          in $dest (if they don't exist) as it goes.  If $src is a file, it copies $src
  204. #          over $dest if $src is newer or if $dest doesn't exist.
  205. # Returns: N/A
  206. Function CompareAndCopy {
  207.     Param (
  208.         [String] $src,
  209.         [String] $dest
  210.     )
  211.     Process {
  212.         # Failsafe: source and destination names must match
  213.         If ($src.split("\")[-1] -eq $dest.split("\")[-1]) {
  214.             $objSrc = Get-Item $src
  215.             If ($objSrc.PSIsContainer) {
  216.                 # Recursion case
  217.                 If (!(Test-Path $dest)) { # Create the destination directory if it doesn't exist
  218.                     New-Item $dest -ItemType Directory
  219.                 }
  220.                 ForEach ($obj In Get-ChildItem $objSrc) {
  221.                     CompareAndCopy -src $obj.FullName -dest (Join-Path $dest -ChildPath $obj.Name)
  222.                 }
  223.             } Else {
  224.                 # Base case
  225.                 If (Test-Path $dest) { # Check modification times, copy if source is newer
  226.                     $objDest = Get-Item $dest
  227.                     If ($objSrc.LastWriteTime -gt $objDest.LastWriteTime) {
  228.                         Copy-Item -Path $src -Destination $dest -Force
  229.                         Write-CopyInfo $objSrc
  230.                     }
  231.                 } Else { # If the file is not in the destination, copy regardless
  232.                     Copy-Item -Path $src -Destination $dest -Force
  233.                     Write-CopyInfo $objSrc
  234.                 }
  235.             }
  236.         } Else {
  237.             CrashAndBurn "Failsafe: $src vs. $dest"
  238.         }
  239.     }
  240. }
  241.  
  242. # ===== MAIN FUNCTION START =====
  243.  
  244. $DTab = "`t`t"
  245. $TTab = "`t`t`t"
  246. $strHeader = "Name $TTab Source Date $TTab Destination Date"
  247. [String] $strNewFileList = ""
  248.  
  249. If (Test-Path $Destination -PathType Container) {
  250.     If (Test-Path $Source) {
  251.         $objSource = Get-Item $Source
  252.         # Check if the source is a file or directory
  253.         If ($objSource.PSIsContainer) {
  254.             $FileMode = $false
  255.         } Else {
  256.             $FileMode = $true
  257.         }
  258.        
  259.         # Check that there are not newer file(s) in the source
  260.         If ($FileMode) {
  261.             Compare-DateModified -src $Source -dest (Join-Path $Destination -ChildPath $objSource.Name)
  262.         } Else {
  263.             Compare-DateModified -src $Source -dest $Destination
  264.         }
  265.         If ($strNewFileList) {
  266.             Write-Host $strHeader
  267.             Write-Host $strNewFileList
  268.             Write-Host "The above files are newer at the destination then at the source."
  269.             Write-Host "You will lose any changes you have made to the destination files"
  270.             Write-Host "if you choose to continue."
  271.             Write-Host ""
  272.             $continue = Request-YNAnswer "Would you like to continue anyway?"
  273.             If (!$continue) {
  274.                 CrashAndBurn "User abort."
  275.             }
  276.         }
  277.        
  278.         If (Confirm-Operation -src $Source -dest $Destination) {
  279.             # Do the initial copy
  280.             If ($FileMode) {
  281.                 Copy-Item -Path $Source -Destination $Destination -Force
  282.             } Else {
  283.                 ForEach ($obj In Get-ChildItem $objSource) {
  284.                     Copy-Item -Path $obj.FullName -Destination $Destination -Recurse -Force
  285.                 }
  286.             }
  287.             Write-Host ""
  288.             Write-Host "The script is now actively backing up your files."
  289.             Write-Host "Press ^C to stop."
  290.             Write-Host ""
  291.            
  292.             # === MAIN BACKUP LOOP ===
  293.             While (Test-Path $Source) {
  294.                 If ($FileMode) {
  295.                     CompareAndCopy -src $Source -dest (Join-Path $Destination -ChildPath $objSource.Name)
  296.                 } Else {
  297.                     ForEach ($obj In Get-ChildItem $objSource) { # Copy each individual thing inside the source into the destination
  298.                         CompareAndCopy -src $obj.FullName -dest (Join-Path $Destination -ChildPath $obj.Name)
  299.                     }
  300.                 }
  301.                 Sleep -Seconds $Interval
  302.             }
  303.             CrashAndBurn "Source $Source disappeared!"
  304.         } Else {
  305.             CrashAndBurn "User abort."
  306.         }
  307.     } Else {
  308.         CrashAndBurn "Source $Source does not exist."
  309.     }
  310. } Else {
  311.     CrashAndBurn "Destination $Destination does not exist or is not a directory."
  312. }
Add Comment
Please, Sign In to add comment