PowerShell Project: Backup Tool
A backup tool is a script that copies files to a backup folder and checks the copy. It proves the copy worked by comparing the file counts.
Copy files with an advanced function
The function takes a source folder and a backup folder. It copies the files and prints both counts.
function Backup-Files {
[CmdletBinding()]
param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
Copy-Item -Path (Join-Path $Source "*.txt") -Destination $Backup
$sourceCount = (Get-ChildItem -Path $Source -File).Count
$backupCount = (Get-ChildItem -Path $Backup -File).Count
"Source: $sourceCount"
"Backup: $backupCount"
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
Backup-Files -Source $source -Backup $backupPreview with -WhatIf instead of copying
With -WhatIf, PowerShell prints a What if line for each file and ShouldProcess returns false. The function then prints a skipped line for each file and copies nothing.
function Backup-Files {
[CmdletBinding(SupportsShouldProcess)]
param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
foreach ($file in (Get-ChildItem -Path $Source -File | Sort-Object Name)) {
if ($PSCmdlet.ShouldProcess($file.Name, "Copy")) { Copy-Item -Path $file.FullName -Destination $Backup } else { "Skipped: $($file.Name)" }
}
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
$null = New-Item -ItemType Directory -Path $backup
Backup-Files -Source $source -Backup $backup -WhatIfVerify the copy with a fixed-format report
After the copy, the script compares the two counts. Equal counts mean the copy is verified.
function Backup-Files {
[CmdletBinding()]
param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
Copy-Item -Path (Join-Path $Source "*.txt") -Destination $Backup
$sourceCount = (Get-ChildItem -Path $Source -File).Count
$backupCount = (Get-ChildItem -Path $Backup -File).Count
"Backup Report"
"Source files: $sourceCount"
"Backup files: $backupCount"
if ($sourceCount -eq $backupCount) { "Verified: Yes" } else { "Verified: No" }
"Copied: " + ((Get-ChildItem -Path $Backup -File | Sort-Object Name | Select-Object -ExpandProperty Name) -join ", ")
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
Backup-Files -Source $source -Backup $backupExample
function Backup-Files {
[CmdletBinding(SupportsShouldProcess)]
param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
foreach ($file in (Get-ChildItem -Path $Source -File | Sort-Object Name)) {
if ($PSCmdlet.ShouldProcess($file.Name, "Copy")) { Copy-Item -Path $file.FullName -Destination $Backup } else { "Skipped: $($file.Name)" }
}
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
$null = New-Item -ItemType Directory -Path $backup
Backup-Files -Source $source -Backup $backup -WhatIf
Backup-Files -Source $source -Backup $backup
$sourceCount = (Get-ChildItem -Path $source -File).Count
$backupCount = (Get-ChildItem -Path $backup -File).Count
"Backup Report"
"Source files: $sourceCount"
"Backup files: $backupCount"
if ($sourceCount -eq $backupCount) { "Verified: Yes" } else { "Verified: No" }
"Copied: " + ((Get-ChildItem -Path $backup -File | Sort-Object Name | Select-Object -ExpandProperty Name) -join ", ")Key points
- Advanced function: a function with CmdletBinding that gains common parameters such as -WhatIf.
- Use SupportsShouldProcess to let a function ask whether an action should run.
- Use ShouldProcess inside the function to run the action or print a skipped line.
- Use ValidateNotNullOrEmpty to reject an empty path before the function runs.
- Use Get-ChildItem -File to list only files and read their count.
- Verification: a check that compares the source count with the backup count.
In this example, the backup function first runs with -WhatIf, so PowerShell prints a What if line for each file and the function prints a skipped line for each file and copies nothing. It then runs for real and copies the files. The driver then prints a fixed-format report that compares the source count with the backup count and lists the copied file names in order.
Note
-WhatIf never copies a file, so a WhatIf run must leave the backup folder empty.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which attribute lets an advanced function support -WhatIf?