Search code examples
powershellregex-greedyprogress-indicator

Powershell script to move files to folders - how do I remove hyphens from $newFolder?


I have a ton of files that I want to file in folders based on the first part of the file name. The file name -> folder name is delimited by specific characters: [-] [,] [and]. Example:

First Filename - 2016-04.pdf Second Filename and another name.mp3 Third.jpg Fourth Filename, 2016.pdf

I want the resulting folder names to NOT include the [\s-] [,\s] [\sand\s] part.

Here is my script, which works, except that the folder names include the stuff I don't want, such as:

First Folder Name - Fourth Filename,

I think it has to do with my regex match being greedy to include the hyphen (in particular) but I am not sure how to format the query and sunbsequent Folder Name creation.

Also, the stuff commented out with "####" (like the progress indicator) doesn't work. If you have any suggestions, PLEASE do comment. I'm not a programmer by any stretch.

$OrigFolder = ".\"
$NewFolder = ".\_Sorted to Move"

# Orphans folder, where files that return null in the regex match will be moved
# Example: file "- title.mp4"
# will be moved to ".\Sorted\_Orphans" folder

$Orphans = '_Orphans' # Use the underscore to sort the folder to the top of the window

#### How to use an array of values for the delimiters in the regex instead of literals
#### My proposed code, but I am missing how o use the delims in the regex match
#### $delims = "\s-\s" ",\s"\s and\s"

# First count the number of files in the $OrigFolder directory
$numFiles = (Get-ChildItem -Path $OrigFolder).Count
$i=0

# Tell the user what will happen
clear-host;
Write-Host 'This script will copy ' $numFiles ' files from ' $OrigFolder ' to _Sorted to Move'

# Ask user to confirm the copy operation
Read-host -prompt 'Press enter to start copying the files'

# Regex to match filenames
$Regex = [regex]"(^\s*(.*?)\s*-)|(^\s*(.*?),)|(^\s*(.*?)\s*and\s)"

    # Loop through the $OrigFolder directory, skipping folders
Get-ChildItem -LiteralPath $OrigFolder | Where-Object {!$_.PsIsContainer} |
    ForEach-Object {
        if($_.BaseName -match $Regex){

            #### Caluclate copy operation progress as a percentage
            #### [int]$percent = $i / $numFiles * 100

            # If first part of the file name is empty, move it to the '_Orphans' folder
            if(!$Matches[1]){
                $ChildPath = $Orphans
            } else {
                $ChildPath =  $Matches[1]
            }

# Generate new folder name
$FolderName = Join-Path -Path $NewFolder -ChildPath $ChildPath

# Create folder if it doesn't exist
    if(!(Test-Path -LiteralPath $FolderName -PathType Container)){
        $null = New-Item -Path $FolderName -ItemType Directory
    }

# Log progress to the screen
Write-Host "$($_.FullName) -> $FolderName"

# Move the file to the folder
            Move-Item -LiteralPath $_.FullName -Destination $FolderName

##### Tell the user how much has been moved
##### Write-Progress -Activity "Copying ... ($percent %)" -status $_  -PercentComplete $percent -verbose
##### $i++

    }
}

Write-Host 'Total number of files in '$OrigFolder ' is ' $numFiles
Write-Host 'Total number of files copied to '$NewFolder ' is ' $i
Read-host -prompt "Press enter to complete..."
clear-host;

And MANY thanks to StackOverflow users for your help and the code snippets I have kludged together above.


Solution

  • Here is the completed script for moving my .pdf files (magazines) to a subdirectory named for the title of the magazine. Thanks to jisaak (Martin Brandl) for helping me resolve the regex issue. The solution to adding 'Magazine' to the new folder is on line 46/47. Probably obvious to most but it took me an hour to figure it out.

    I would love suggestions for streamlining the code and fixing the indents, etc. I am not style-aware with Powershell yet and it's my first attempt

    $OrigFolder = "T:\Magazines"
    $NewFolder = "T:\Magazines\_Sorted to Move"
    
    # Orphans folder, where files that return null in the regex match will be moved
    # Example: file "- title.pdf"
    # will be moved to ".\Sorted\_Orphans" folder
    
    $Orphans = '_Orphans' # Use the underscore to sort the folder to the top of the window
    
    #### How to use an array of values for the delimiters in the regex instead of literals
    #### My proposed code, but I am missing how o use the delims in the regex match
    #### $delims = "\s-\s" ",\s"\s and\s"
    
    # First count the number of files in the $OrigFolder directory
    $numFiles = (Get-ChildItem -Path $OrigFolder).Count
    $i=0
    
    # Tell the user what will happen
    clear-host;
    Write-Host 'This script will copy ' $numFiles ' files from ' $OrigFolder ' to _Sorted to Move'
    
    # Ask user to confirm the copy operation
    Read-host -prompt 'Press enter to start copying the files'
    
    # Regex to match filenames
    $Regex = [regex]"(?:(.*?)\s-)|(?:(.*?),\s)|(?:(.*?)\sand\s)"
    
    # Loop through the $OrigFolder directory, skipping folders
    Get-ChildItem -LiteralPath $OrigFolder | Where-Object {!$_.PsIsContainer} |
        ForEach-Object {
            if($_.BaseName -match $Regex){
            $ChildPath = $_.BaseName -replace $Regex
    
    #Caluclate copy operation progress as a percentage
    [int]$percent = $i / $numFiles * 100
    
    
    # If first part of the file name is empty, move it to the '_Orphans' folder
    if(!$Matches[1]){
        $ChildPath = $Orphans}
    else {
        $ChildPath = $Matches[1]
        }
    
    
    # Generate new folder name and append ' Magazine' to the new folder name
    $FolderName = Join-Path -Path $NewFolder -ChildPath ($ChildPath + ' Magazine')
    
    # Create folder if it doesn't exist
        if(!(Test-Path -LiteralPath $FolderName -PathType Container)){
        $null = New-Item -Path $FolderName -ItemType Directory}
    
    # Log progress to the screen
    Write-Host "$($_.FullName) -> $FolderName"
    
    # Move the file to the folder
    Move-Item -LiteralPath $_.FullName -Destination $FolderName
    
    # Tell the user how much has been moved
    Write-Progress -Activity "Copying ... ($percent %)" -status $_  -PercentComplete $percent -verbose
    $i++
        }
    }
    
    Write-Host 'Total number of files in '$OrigFolder ' is ' $numFiles
    Write-Host 'Total number of files copied to '$NewFolder ' is ' $i
    Read-host -prompt "Press enter to complete..."
    clear-host;
    

    I've modded the script for my multimedia files and pictures as well and you have NO IDEA how much time this will save me. THANK YOU AGAIN, Martin!