quarkz
quarkz

Reputation: 17

Powershell: Recursively Move Files From Source Directory To One-Level Up

I've just started dabbling with Powershell & after searching many scripts, I have not found one that works. (Maybe I'm not running correctly, but I have enabled the right administrative rights, etc.)

My request:
A Powershell script which
- recursively goes through directory structure, to the last level
- moves all content (files with any extension), UP one-level, from where the files were
- after the move, removes that folder the files were moved from

    ◘ BEFORE ◘

    Classical
    ├─Mozart-5
    │ └─Mozart-European-Composers
    │   ├─01-symphony.mp3
    │   └─03-symphony.mp3
    │
    ├─Bach-7
    │ └─Bach
    │   ├─02-symphony.ogg
    │   └─04-concerto.wav
    │
    │
    └─Vivaldi-2
      └─Vivaldi-Not-The-Browser
        ├─01-track.m4a
        └─02-solo.mp4

    =================================================

    ◘ AFTER ◘

    Classical
    ├─Mozart-5
    │ ├─01-symphony.mp3
    │ └─03-symphony.mp3
    │
    ├─Bach-7
    │ ├─02-symphony.ogg
    │ └─04-concerto.wav
    │
    └─Vivaldi-2
      ├─01-track.m4a
      └─02-solo.mp4



Notes:
- Base-folder could be called any folder, e.g. Classical, Rock, etc.
(so, ideally, I would run the Powershell script from inside the base-folder, where all the sub-folders are)
- Folder depth could be 1 or many levels deep
( 99.9% of the time, it will be 2-3 levels deep, i.e.
Root - Music
1st - _Classical_
2nd - Mozart-5
3rd - Mozart-European-Composers

1st - _Indian Classical_
2nd - Ravi
3rd - Ravi Masterpieces
2nd - Zakir
3rd - Zakir-Solo-Piece )


Upvotes: 2

Views: 1597

Answers (3)

RetiredGeek
RetiredGeek

Reputation: 3158

Ok this had me going for a while but I finally figured it out. It required a Recursive Function (a function that calls itself). This method doesn't care how deep your tree is so it will handle that .1% of cases that go more than 3 levels deep.

Clear-Host

Function Check-Dir {

  Param (
    [Parameter(Mandatory=$True)]
      [String] $Path
  )

  $GCIArgs = @{Path = "$Path"}

  $Files = Get-ChildItem @GCIArgs | 
             Where-Object {-not $_.PSIsContainer}

#--- If no Subs returns $Null! ---
  $Subs  = Get-childitem @GCIArgs | 
             Where-Object {$_.PSIsContainer}

  If ($Null -ne $Subs) {
    ForEach ($S in $Subs) {
      $Bottom = $False
      $DelDir = Check-Dir -Path "$($S.FullName)"
      If ($DelDir) { 
        $S | Remove-Item -Force 
      }
    }
  }

  Else {
   $MIArgs = @{Destination = "$("$Path" + "\..")"
               Force       = $True}
   $Files | Move-Item @MIArgs
   $Bottom = $True         
  }

  If ($Bottom) {$True}
  Else         {$False}

} #End Function Check-Dir

#-------------- Main Program -----------------

$BaseDir = "G:\Test\Music"

Check-dir -path "$BaseDir"

HTH

Upvotes: 1

quarkz
quarkz

Reputation: 17

I was able to mix'n'match up a solution, which works for me, ie.
A 2-step process:
1) Move all files 1-level up, to its parent
2) Since the source folder is now empty, because the files were moved 1-level up, so next task is to recursively remove all of the empty folders

With this in mind:

Get-ChildItem '*\*\*' | Move-Item -Destination { $_.Directory.Parent.FullName }
Get-ChildItem -recurse | Where {$_.PSIsContainer -and `
@(Get-ChildItem -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
Remove-Item -recurse

Notes:
○ I should have also mentioned that my folder names contained "[" and "]", and this becomes challenging for Powershell, because Get-ChildItem won't work.
See here: Option -recurse sometimes does not work for PowerShell cmdlet get-childitem

○ Also, because of "[" and "]" in myfolder names, removing these folders also require some 'trickery'.
At first, I settled on this solution
- How to recursively remove all empty folders in PowerShell?
However, this is the one that worked well:
How to delete empty subfolders with PowerShell?
(Inspired by: PowerShell script to recursively delete empty folders )

Upvotes: 0

RetiredGeek
RetiredGeek

Reputation: 3158

This worked in my tests.

Clear-Host

$BaseDir = "G:\Test\America"

$GCIArgs = @{Path    = "$BaseDir"
             Exclude = "*.ini"
             Recurse = $True}
$Files = Get-childitem @GCIArgs
$Files | Where-Object {-not $_.PSIsContainer} |
         Move-Item     -Destination "$BaseDir" 

$Files | Where-Object {$_.PSIsContainer} |
         Remove-Item -Force -Recurse

Note: you can get rid of the Exclude argument if you want to save the .ini files.

HTH

Upvotes: 0

Related Questions