Home > other >  Powershell - Sort files into directory based on file name
Powershell - Sort files into directory based on file name

Time:09-10

I'm trying to sort my files into unique dictionaries based on the file name and delimiters.

File names follow this structure: LocationName-DP-Category-S-L

Where... - is the delimiter

  • Location name is a variable length, never has spaces
  • DP is the two-letter department code
  • Category is a variable length, never has spaces
  • S is a single-letter size code
  • L is length

Everything after category is irrelevant, as there is often some extra garble at the end of the filename to ignore.

I'd like to have a folder structure created and sorted for every found variation of just the LocationName-DP-Category portion of the filepath that looks something like this:

  • Parent Folder: LocationName
  • SubFolder: DP
  • SubSubFolder: Category

Trying to keep it less rigid as this runs often on various batches of files. Here's what I have:

##GET USER INPUT FROM DIALOG
Add-Type -AssemblyName System.Windows.Forms
$browser = New-Object System.Windows.Forms.FolderBrowserDialog
$null = $browser.ShowDialog()
$path = $browser.SelectedPath   "\"

##SORT ALL FILES
Get-ChildItem -Path $path *-*-*-*-*.* |
  Where-Object BaseName -match '(?<store>\w )\- (?<dept>\w )\- (?<category>\w )*.*'|
    Group-Object {$Matches["store"] '-' $Matches["dept"] '-' $Matches["Category"]}|
      ForEach-Object{mkdir $_.Name;$_.Group|Move-Item -Dest $_.Name}

Would really appreciate the help, been scratching my head on and off for a couple months.

Thanks

CodePudding user response:

...initially I also thought about group-object but I think that is not necessary, this is simpler:

#Get Source Files
$files = get-childitem -Path [path]
#Set target root path
$targetPathRoot = "C:\tmp\"
#Loop through array of files
foreach ($file in $files){
    #Split the filename 
    $split = $file.name -split "-"
    #Build the target path
    $newFullPath = $targetPathRoot   $split[0]   '\'   $split[1]   '\'   $split[2]
    #If path exists move item, otherwise create path and move item
    If (Test-Path $newFullPath){
        $null = move-item -Path $file.psPath -Destination $newFullPath
    }
    Else {
        $null = new-item -ItemType Directory -Path $newFullPath -Force
        $null = move-item -Path $file.psPath -Destination $newFullPath
    }
}

CodePudding user response:

Assuming none of the parts 'LocationName', 'DP' or 'Category' contain a hyphen, you could create the new path for the file like this:

# 'LocationName-DP-Category-S-L-blah.ext' --> LocationName\DP\Category
$_.BaseName -replace '^(\w -\w{2}-\w ).*', '$1' -replace '-', '\'

Your code can then become

Add-Type -AssemblyName System.Windows.Forms
$browser = New-Object System.Windows.Forms.FolderBrowserDialog
$null = $browser.ShowDialog()
$path = $browser.SelectedPath
$browser.Dispose()  # remove from memory when done

# the root path for the (new) subfolders and files
$destination = 'X:\SomeWhere'
if (![string]::IsNullOrWhiteSpace($path)) {
    Get-ChildItem -Path $path -Filter '*-*-*-*-*.*' -File | ForEach-Object {
        $subPath   = $_.BaseName -replace '^(\w -\w{2}-\w ).*', '$1' -replace '-', '\'
        $targetDir = Join-Path -Path $destination -ChildPath $subPath
        # create the new path if it does not already exist
        $null = New-Item -Path $targetDir -ItemType Directory -Force
        # move the file
        $_ | Move-Item -Destination $targetDir
    }
}
else {
    Write-Host "Folder dialog cancelled"
}

Note: On the File System, the New-Item -Path $targetDir -ItemType Directory -Force will either create the new folder, OR return a reference to an existing folder. The -Force switch in this case allows you not to have to use Test-Path in this case. Mind you, you should NOT use that technique when dealing with other systems like on registry keys for instance

  • Related