2

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 )


quarkz
  • 17
  • 3
  • 1
    when you get a list of file objects with `Get-ChildItem`, each will have a `.Directory` prop that is the current dir. that prop will have a `.Parent` prop that is the parent dir of the one the file is in. you can use that as your "move to" destination. [*grin*] – Lee_Dailey Jun 17 '20 at 20:44

3 Answers3

1

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

RetiredGeek
  • 2,980
  • 1
  • 7
  • 21
  • FYI: I found out you can increase the efficiency of the above program by changing Get-ChildItem to Get-Item and the adjusting the $GCIArgs = @{Path = "$Path"} to $GCIArgs = @{Path = "$Path"\*}. Get-Item returns much less information but still has the required information. – RetiredGeek Jun 19 '20 at 15:25
  • that's actually not bad :-) It worked, except: I got a small error,...something about 'recursion' parameter needed to be specified, so i said YES to that error message and it worked. _KIND OF_ :-) For a folder that was named " 01-[0009]-23\xyz\*.* ", your script removed EVERYTHING , and so i was only left with " 01-[0009]-23 " (ie. \\xyz and xyz\\*.* were removed completely ). See, there's a thing that's funny about GCI with folders with 'square brackets', which I mentioned at the top. – quarkz Jun 19 '20 at 22:21
  • 1
    Sorry, but it's gonna take someone brighter than me to solve that one. Don't know why the [ character fouls things but I can't find a way to filter it out and still make the recursion work properly. The program does work properly on normally named directories. I remember from the old days when we were admonished to never use special characters in any part of a filespec. Now I know why! – RetiredGeek Jun 20 '20 at 00:55
  • have a look at the comment from @Alexander Groß here -- https://stackoverflow.com/questions/1575493/how-to-delete-empty-subfolders-with-powershell -- about the solutions for folders containing `[]`. The solution escapes me, as I'm a newbie to Powershell (but not computers). Otherwise, this could be resolved with RegEx (Regular Expressions) :-) – quarkz Jun 20 '20 at 16:20
0

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

RetiredGeek
  • 2,980
  • 1
  • 7
  • 21
  • i had 3 x folders [A, B, C], and in each folder [A,B,C], i had another folder [A2, B2, C2]. your script @RetiredGeek, just moved all files, to the root directory and then deleted ALL folders (A\A2, B\B2, C\C2). i was expecting all files inside A\A2\*.* to move to A\*.* and A2 removed. – quarkz Jun 17 '20 at 22:37
  • Sorry, I misunderstood. – RetiredGeek Jun 18 '20 at 00:30
0

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 )

quarkz
  • 17
  • 3