Skip to content

Instantly share code, notes, and snippets.

@wilfriedwoivre
Created November 5, 2025 14:04
Show Gist options
  • Select an option

  • Save wilfriedwoivre/1286ea2daf0620294d3a88e12c15a8dd to your computer and use it in GitHub Desktop.

Select an option

Save wilfriedwoivre/1286ea2daf0620294d3a88e12c15a8dd to your computer and use it in GitHub Desktop.
MoveDirectoryRecursive
function Move-ItemsRecursively {
<#
.SYNOPSIS
Moves files and folders recursively, creating destination folders as needed.
.DESCRIPTION
This function moves all files and folders from a source path to a destination path,
maintaining the directory structure. It creates any necessary nested folders at the destination.
.PARAMETER SourcePath
The source directory path to move from
.PARAMETER DestinationPath
The destination directory path to move to
.EXAMPLE
Move-ItemsRecursively -SourcePath "C:\Source" -DestinationPath "D:\Destination"
.EXAMPLE
Move-ItemsRecursively -SourcePath "C:\Source" -DestinationPath "D:\Destination" -WhatIf
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path $_ })]
[string]$SourcePath,
[Parameter(Mandatory = $true)]
[string]$DestinationPath
)
begin {
# Resolve full paths
$SourcePath = Resolve-Path $SourcePath | Select-Object -ExpandProperty Path
# Create destination root if it doesn't exist
if (-not (Test-Path $DestinationPath)) {
if ($PSCmdlet.ShouldProcess($DestinationPath, "Create destination directory")) {
New-Item -Path $DestinationPath -ItemType Directory -Force | Out-Null
Write-Host "Created destination directory: $DestinationPath" -ForegroundColor Green
}
}
$DestinationPath = Resolve-Path $DestinationPath | Select-Object -ExpandProperty Path
$movedCount = 0
$errorCount = 0
}
process {
Write-Host "`nStarting recursive move from:" -ForegroundColor Cyan
Write-Host " Source: $SourcePath" -ForegroundColor Yellow
Write-Host " Destination: $DestinationPath" -ForegroundColor Yellow
Write-Host ""
# Get all items recursively (files and directories)
$allItems = Get-ChildItem -Path $SourcePath -Recurse -Force
# Separate directories and files
$directories = $allItems | Where-Object { $_.PSIsContainer } | Sort-Object FullName
$files = $allItems | Where-Object { -not $_.PSIsContainer }
Write-Host "Found $($directories.Count) directories and $($files.Count) files to move`n" -ForegroundColor Cyan
# Create all directory structures first
foreach ($dir in $directories) {
$relativePath = $dir.FullName.Substring($SourcePath.Length).TrimStart('\', '/')
$destDir = Join-Path $DestinationPath $relativePath
if (-not (Test-Path $destDir)) {
if ($PSCmdlet.ShouldProcess($destDir, "Create directory")) {
try {
New-Item -Path $destDir -ItemType Directory -Force | Out-Null
Write-Host "[DIR] Created: $relativePath" -ForegroundColor Green
}
catch {
Write-Host "[ERROR] Failed to create directory: $relativePath - $($_.Exception.Message)" -ForegroundColor Red
$errorCount++
}
}
}
}
Write-Host ""
# Move all files
foreach ($file in $files) {
$relativePath = $file.FullName.Substring($SourcePath.Length).TrimStart('\', '/')
$destFile = Join-Path $DestinationPath $relativePath
$destDir = Split-Path $destFile -Parent
# Ensure the destination directory exists
if (-not (Test-Path $destDir)) {
if ($PSCmdlet.ShouldProcess($destDir, "Create directory")) {
New-Item -Path $destDir -ItemType Directory -Force | Out-Null
}
}
if ($PSCmdlet.ShouldProcess($file.FullName, "Move to $destFile")) {
try {
Move-Item -Path $file.FullName -Destination $destFile -Force
Write-Host "[FILE] Moved: $relativePath" -ForegroundColor Cyan
$movedCount++
}
catch {
Write-Host "[ERROR] Failed to move file: $relativePath - $($_.Exception.Message)" -ForegroundColor Red
$errorCount++
}
}
}
# Remove empty directories from source (bottom-up)
$directories | Sort-Object FullName -Descending | ForEach-Object {
if ((Test-Path $_.FullName) -and ((Get-ChildItem $_.FullName -Force).Count -eq 0)) {
if ($PSCmdlet.ShouldProcess($_.FullName, "Remove empty directory")) {
try {
Remove-Item $_.FullName -Force
}
catch {
Write-Host "[WARNING] Could not remove empty directory: $($_.FullName)" -ForegroundColor Yellow
}
}
}
}
# Remove source directory if empty
if ((Test-Path $SourcePath) -and ((Get-ChildItem $SourcePath -Force).Count -eq 0)) {
if ($PSCmdlet.ShouldProcess($SourcePath, "Remove empty source directory")) {
try {
Remove-Item $SourcePath -Force
Write-Host "`nRemoved empty source directory" -ForegroundColor Green
}
catch {
Write-Host "`n[WARNING] Could not remove source directory: $SourcePath" -ForegroundColor Yellow
}
}
}
}
end {
Write-Host "`n" + ("=" * 50) -ForegroundColor Cyan
Write-Host "Move operation completed!" -ForegroundColor Green
Write-Host " Files moved: $movedCount" -ForegroundColor Green
Write-Host " Errors: $errorCount" -ForegroundColor $(if ($errorCount -gt 0) { "Red" } else { "Green" })
Write-Host ("=" * 50) -ForegroundColor Cyan
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment