if-statementmovesubdirectorypowershell-7.0

PowerShell 5 (now updated to 7) - Check if multiple levels / depths of subfolders exists and if so move its contents up one level


Normally I wouldn't even post without some framework together, but I'm stumped on where to begin.

I have a directory of hundreds of subfolders that should only be one level deep but some are two levels deep:

I need to recursively go through the MainDirectory, and if there's a second sublevel of files, then move just those files up one level (so that SubfolderC1's files are actually placed in SubfolderC) and then those files need to have SubFolderC1's name appended to the front of the file.

Ex:

would become:

I don't know the subfolders or filenames ahead of time, they could be named anything.

The following feels like a start.

$source = "C:\MainDirectory"

$levels = Get-ChildItem $source -Directory -Recurse | ForEach-Object {$_.PSPath.Split('\').Count}
if ($levels -gt 3) {
}

But I don't know how to use the results for anything meaningful.

Editing to add progress. This seems to at least tell me what directory exceeds the threshold:

Get-ChildItem $source -Directory -Recurse | ForEach-Object {$path = $_.FullName; $levels = $_.PSPath.Split('\').Count;
if ($levels -gt 3){
Write-Output $path
} 
}

Now I just need to use $path to get the corresponding child file, move it up a directory, rename it based off of its former parent folder.

Edited - Progress has been made:

$source = "C:\MainDirectory"

Get-ChildItem $source -Recurse | ForEach-Object {
     $components = $_.FullName.Split("\")
     $levels = $components.Count
     if ($levels -gt 3) {
         Write-Output $_.FullName 
     }
}

Now I just need to move the files up one level and rename them.

ETA: Last Revision for the time being. I hate to do it, but if I just re-run the script at the end then I can move the files up another directory with Split-Path function:

$gparentdir = Split-Path (Split-Path -parent $_.FullName) -Parent

The whole process is dirty and inelegant and any cleaning up would be great but I'll paste what I got as my answer.


Solution

  • The first part is counting the number of directories to execute the code on in the first place. Using Split to count the number of backslashes in the filename helps get how deep a file can be. Then any file that meets the criteria is being renamed to include its parent name, i.e.

    C:\MainDirectory\SubfolderC\SubfolderC1\FileA.type

    becomes:

    C:\MainDirectory\SubfolderC\SubfolderC1\SubfolderC1FileA.type

    Then we're repeating the process to move the child up to its parent folder so it now becomes

    C:\MainDirectory\SubfolderC\SubfolderC1FileA.type

    $source = "C:\MainDirectory"
    
    Get-ChildItem $source -Recurse | ForEach-Object {
    
       # Split the file path into individual directory components
         $components = $_.FullName.Split("\")
    
       # Count the number of components to determine the depth of the file's directory
         $levels = $components.Count
    
       # If the depth is greater than the threshold, rename the file based on its parent directory name 
         if ($levels -gt 3) {
             $parentName = $_.Directory.Name
             $newName = "{0}{1}" -f $parentName, $_.Name
             Rename-Item $_.FullName -NewName $newName
        } 
         
    }
    
       # Same script as above to count the number of backslashes in the filename
    
    Get-ChildItem $source -Recurse | ForEach-Object {
     
         $components = $_.FullName.Split("\") 
         $depth = $components.Count 
    
         if ($depth -gt 3) {
             $parentdir = Split-Path $_.FullName -Parent
             Move-Item $_.FullName -Destination $parentdir
        }
        
    }
    
    

    You can also use Split-Path to up 2 directories to the "grand parent" directory if you need to.

    $gparentdir = Split-Path (Split-Path -parent $_.FullName) -Parent