Created
November 5, 2025 14:04
-
-
Save wilfriedwoivre/1286ea2daf0620294d3a88e12c15a8dd to your computer and use it in GitHub Desktop.
MoveDirectoryRecursive
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
| 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