Skip to content

Instantly share code, notes, and snippets.

@Fireforge
Last active April 2, 2024 15:34
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save Fireforge/f80a5a3a3afb039a2c11ea662be97866 to your computer and use it in GitHub Desktop.
Save Fireforge/f80a5a3a3afb039a2c11ea662be97866 to your computer and use it in GitHub Desktop.
Powershell backup script using Robocopy
#
# ROBOCOPY Script
#
# This script calls robocopy over a few folders, and then emails you when they're done
Param(
[Parameter(Mandatory=$true)]
[string]$basesrc, #"G:\",
[Parameter(Mandatory=$true)]
[string]$basedst, #
[Parameter(Mandatory=$false)]
[string[]]$dirs= $(),
[string]$backup_log_dir = "backuplogs"
)
Set-Location -Path $PSScriptRoot
Write-Host "###### Starting backup script ######"
$robocopy_block = {
param($src,$dst,$log)
New-Item -Force $log | Out-Null
# Execute a command
robocopy $src $dst /MIR /ZB /MT /XJ /XD Temp /R:3 /LOG:$log /NP
# /MIR # mirroring the folders, removing non-existant files. We don't want lots of old files cluttering up the backup and the File Shares should keep a history on their own
# /ZB # copies files such that if they are interrupted part-way, they can be restarted (should be default IMHO)
# /MT # copies using multiple cores, good for many small files
# /XJ # Ignoring junctions, they can cause infinite loops when recursing through folders
# /XD Temp # Temp directories shouldn't have important data files
# /R:3 # Retrying a file only 3 times, we don't want bad permissions or other dumb stuff to halt the entire backup
# /LOG:$log # Logging to a file internally, the best way to log with the /MT flag
# /NP # Removing percentages from the log, they don't format well
"Backup directory is complete. [$src -> $dst] ($log)"
}
if ((test-path variable:\dirs) -and ($dirs.Length -ne 0)) {
foreach ($dir in $dirs)
{
$src = Join-Path $basesrc $dir
$dst = Join-Path $basedst $dir
$log = Join-Path $backup_log_dir "backup_$($dir)_$(get-date -f yyyy-MM-dd_hh-mm-ss).log"
# Show the loop variable here is correct
Write-Host "Backing up $dir..."
# pass the loop variable across the job-context barrier
Start-Job $robocopy_block -ArgumentList $src,$dst,$log -Name "backup-$dir" | Out-Null
}
}
else {
Write-Host "Backing up $basesrc -> $basedst..."
$log = Join-Path $backup_log_dir "backup_$(get-date -f yyyy-MM-dd_hh-mm-ss).log"
Invoke-Command $robocopy_block -ArgumentList $basesrc,$basedst,$log | Out-Null
}
# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }
# Display output from all jobs
Get-Job | Receive-Job
# Cleanup
Remove-Job *
Send-MailMessage -to "admin <admin@foo.com>" `
-from "backup <backup@foo.com>" `
-subject "Backup Finished" `
-smtpServer smtp.foo.com
Write-Host "###### Backup is complete ######"
@georgeeerenee
Copy link

I'm not really good at using command line interfaces. So I use GS Rich Copy 360 for that purpose. It has all the features of RoboCopy and a bit more.

@UniDyne
Copy link

UniDyne commented Apr 14, 2021

Warning: Given a list of source directories, this script will start all the instances of Robocopy in parallel, potentially clobbering your source and backup disks until the backup is complete.

@trossr32
Copy link

trossr32 commented Apr 2, 2024

@Fireforge Thanks for this, helped me implement a backup script for myself. FYI it seems robocopy copies across folder attributes, so when a source directory is a drive root and the destination directory isn't, the destination directories are then hidden. Line 94 of my fork fixes this if you're interested 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment