How to merge / 'flatten' a folder structure using PowerShell - recursive
Here's a single-pipeline solution:
$targetDir = Convert-Path '.' # Get the current (target) directory's full path.Get-ChildItem -LiteralPath $targetDir -Directory | # Loop over child dirs.Get-ChildItem -Recurse -File -Filter *.txt | # Loop over all *.txt files in subtrees of child dirs.Move-Item -Destination { # Move to target dir. # Construct the full target path from the target dir. # and the relative sub-path with path separators replaced with "_" chars. Join-Path $targetDir ` ($_.Fullname.Substring($targetDir.Length + 1) -replace '[/\\]', '_') } -Whatif
-WhatIf
previews the move operations; remove it to perform actual moving.
Regex [/\\]
matches /
or \
as the path separator, so as to make the solution cross-platform.
while it would have been nice if you had shown what you have tried, i got interested ... [grin]
what the code does ...
- sets some constants
- creates some files & dirs to work with
- grabs the files that match the target location & type
- skips any file that is already in the $TopDir
- derives the old file & dir names
- uses the above to make a new full file name
- moves the items
this only shows what WOULD happen. remove the-WhatIf
to do it for real.
please note that the code DOES NOT check to see if there are same-named files in the destination.
the code itself ...
$TopDir = "$env:TEMP\PlexStuff"$Delimiter = '_-_'$Filter = '*.txt'#region >>> create some files to test with# remove this region after you have tested it@( [System.IO.FileInfo]"$TopDir\OneThing\aaa.txt" [System.IO.FileInfo]"$TopDir\OneThing\bbb.txt" [System.IO.FileInfo]"$TopDir\TwoThing\aaa.txt" [System.IO.FileInfo]"$TopDir\ThreeThing\ccc.txt" [System.IO.FileInfo]"$TopDir\ThreeThing\ddd.txt" [System.IO.FileInfo]"$TopDir\eee.txt" ) | ForEach-Object { # the "$Null =" suppresses unwanted output from the commands $Null = mkdir $_.Directory.FullName -Force -ErrorAction SilentlyContinue $Null = New-Item -Path $_.Directory.FullName -Name $_.Name -ItemType File -ErrorAction SilentlyContinue }#endregion >>> create some files to test with$FileList = Get-ChildItem -LiteralPath $TopDir -File -Recurse -Filter $Filterforeach ($FL_Item in $FileList) { # skip files that are in the TopDir if (-not ($FL_Item.DirectoryName -eq $TopDir)) { $OldFileName = $FL_Item.Name $OldDirName = $FL_Item.DirectoryName $NewFileName = ($OldDirName, $OldFileName) -join $Delimiter Move-Item -LiteralPath $FL_Item.FullName -Destination $NewFileName -WhatIf } }
output [slightly reformatted for readability] ...
What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\OneThing\aaa.txt Destination: C:\Temp\PlexStuff\OneThing_-_aaa.txt".What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\OneThing\bbb.txt Destination: C:\Temp\PlexStuff\OneThing_-_bbb.txt".What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\ThreeThing\ccc.txt Destination: C:\Temp\PlexStuff\ThreeThing_-_ccc.txt".What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\ThreeThing\ddd.txt Destination: C:\Temp\PlexStuff\ThreeThing_-_ddd.txt".What if: Performing the operation "Move File" on target "Item: C:\Temp\PlexStuff\TwoThing\aaa.txt Destination: C:\Temp\PlexStuff\TwoThing_-_aaa.txt".