简体   繁体   中英

Powershell - Sort files into directory based on file name

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-SL

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

...initially I also thought about group-object but I think that is not necessary, think 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
    }
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM