Initializing
Back to Projects
Year2024
DomainFullstack
AccessOpen Source
Complexity0 / 10
PowerShellWindowsAutomationFile Management
FullstackArchived

PowerShell Folder Organizer

A PowerShell script that automatically reorganizes messy folder structures into a clean, categorized hierarchy with logging and safe move operations.

# PowerShell Folder Organizer

A robust PowerShell automation script that reorganizes chaotic folder structures into a clean, categorized hierarchy. Originally designed for "Note Nest" personal knowledge management system, it can be adapted for any folder reorganization task.

Purpose and Problem

The Problem

Personal knowledge management systems often become messy over time:

  • Files scattered across multiple folders
  • Inconsistent naming conventions
  • No clear organization structure
  • Difficult to find anything

The Solution

This script automatically:

  1. Creates a new organized folder structure
  2. Categorizes files based on naming patterns
  3. Handles special cases (date-based folders, project files)
  4. Logs all operations for review
  5. Cleans up empty source directories

Folder Structure Created

Parsing system architecture diagram...

New Structure Definition

FolderPurpose
00_InboxUncategorized/new files awaiting sorting
01_ProjectsClient projects, contracted work
02_AreasOngoing areas of responsibility
03_ResourcesReference materials, templates
04_ArchivesCompleted/deprecated items
05_JournalDaily logs, time-based entries
06_AssetsMedia files, images, diagrams
07_TempTemporary working files

Core Features

1. Safe Move Operations

powershell
function Move-ItemSafely {
    param (
        [string]$RelativeSourcePath, 
        [string]$DestinationFolder, 
        [string]$NewName = ''
    )
    
    $SourceFullPath = Join-Path $RootPath $RelativeSourcePath
    $DestFullPath = Join-Path $RootPath $DestinationFolder
    
    # Create destination if it doesn't exist
    if (-not (Test-Path $DestFullPath)) { 
        New-Item -Path $DestFullPath -ItemType Directory -Force | Out-Null 
    }
    
    # Determine final destination
    $FinalDest = if ($NewName) { 
        Join-Path $DestFullPath $NewName 
    } else { 
        Join-Path $DestFullPath (Split-Path $SourceFullPath -Leaf) 
    }
    
    try {
        Move-Item -LiteralPath $SourceFullPath -Destination $FinalDest -Force -ErrorAction Stop
        Add-Content -Path $LogFile -Value "MOVED: $RelativeSourcePath `n   TO: $FinalDest"
    } catch {
        Add-Content -Path $LogFile -Value "ERROR moving $RelativeSourcePath : $($_.Exception.Message)"
    }
}

Features:

  • Creates destination folders automatically
  • Handles name conflicts with -Force
  • Logs both success and errors
  • Uses -LiteralPath for special characters in paths

2. Comprehensive Logging

powershell
$LogFile = Join-Path $RootPath "reorganization_log_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt"

Every operation is logged with:

  • Timestamp
  • Source path
  • Destination path
  • Errors (if any)

Log Example:

code
--- Reorganization Log: 2025-06-30 10:15:32 ---
MOVED: Note Nest\Collections\About Us.html 
   TO: E:\localhost\...\01_Projects\Namani_Construction\Website_Development\About Us.html
MOVED: Note Nest\June\30-06-2024\Daily Log.md 
   TO: E:\...\05_Journal\2024\06_June\30-06-2024_Daily_Log_Daily Log.md
--- Reorganization Complete: 2025-06-30 10:16:45 ---

3. Pattern-Based File Routing

The script uses wildcard pattern matching to categorize files:

powershell
switch -Wildcard ($_.Name) {
    'About Us.html'         { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\Namani_Construction\Website_Development" }
    'blog.html'             { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\Namani_Construction\Website_Development" }
    '*ASK BASKET*'          { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "04_Archives\Historical_Client_Docs" }
    'AI Learning Roadmap*'  { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "02_Areas\AI_Automation" }
    'Pasted image*.png'     { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "06_Assets\Images" }
    'Untitled*'             { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "00_Inbox" }
    default                 { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "00_Inbox" }
}

4. Special Folder Handling

#### June Date-Based Folder Processing

powershell
$juneRoot = Join-Path $RootPath "Note Nest\June"
if (Test-Path $juneRoot) {
    Get-ChildItem -Path $juneRoot -Directory | ForEach-Object {
        $dateFolder = $_
        if ($dateFolder.Name -match '(\d{2})-(\d{2})-(\d{4})') {
            $year = $Matches[3]
            $month = $Matches[2]
            $day = $Matches[1]
            $monthName = (Get-Culture).DateTimeFormat.GetMonthName([int]$month)
            $destJournalFolder = "05_Journal\$year\$($month)_$monthName"
            
            Get-ChildItem -Path $dateFolder.FullName -File | ForEach-Object {
                $file = $_
                $fileRelativePath = $file.FullName.Substring($RootPath.Length + 1)
                $newLogFileName = "$($year)-$($month)-$($day)_Daily_Log_$($file.Name)"
                Move-ItemSafely -RelativeSourcePath $fileRelativePath -DestinationFolder $destJournalFolder -NewName $newLogFileName
            }
        }
    }
}

Converts: Note Nest\June\30-06-2024\Daily Log.md To: 05_Journal\2024\06_June\30-06-2024_Daily_Log_Daily Log.md

5. Folder Filtering

Files in certain directories are skipped to prevent duplicates:

powershell
if ($relativePath -like '*\Site Structure\*' -or $relativePath -like '*\June\*') { return }

6. Source Directory Cleanup

After reorganization, empty source directories are removed:

powershell
@( "Collections", "Note Nest", "Prompt Wrapper", "Scripts" ) | ForEach-Object {
    $dirPath = Join-Path $RootPath $_
    if (Test-Path $dirPath) {
        try { Remove-Item -Path $dirPath -Recurse -Force -ErrorAction Stop } catch {}
    }
}

Usage

Prerequisites

powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force

Running the Script

powershell
.\reorganize_PERFECT.ps1 -RootPath "E:\localhost\Note Nest_COPY"

Or use the launcher:

powershell
cd C:\Users\bhargavxharma\OneDrive\Desktop
.\reorganize_PERFECT.ps1 -RootPath "E:\localhost\Note Nest_COPY"

Parameters

ParameterRequiredDescription
RootPathYesPath to the root folder to reorganize

File Categorization Examples

File PatternDestination
HTML files for clients01_Projects\{Client}\Website_Development
AI/Learning notes02_Areas\AI_Automation
Frontend code snippets02_Areas\Code_Development\Frontend_UI_Snippets
Server admin docs02_Areas\Linux_Server_Administration
Prompt templates03_Resources\Prompt_Templates
Personal reference03_Resources\Personal_Reference
Deprecated code04_Archives\Deprecated_Code_Dev_Playground
Image files06_Assets\Images
SVG diagrams06_Assets\Diagrams_SVGs
Untitled/Unknown00_Inbox

Safety Features

  1. Relative Paths: All operations use relative paths from RootPath
  2. Existence Checks: Verify files/folders exist before moving
  3. Error Handling: Try-catch blocks around all Move-Item operations
  4. Logging: Complete audit trail of all operations
  5. Destination Creation: Auto-create missing destination folders
  6. No Delete: Only moves files, never deletes content

Customization Guide

Adding New File Patterns

Add new cases to the switch statement:

powershell
'YourPattern*' { 
    Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "Target_Folder" 
}

Changing Folder Structure

Modify the folder creation section:

powershell
# Create new structure
@( "00_Inbox", "01_Projects", "02_Areas", "03_Resources", "04_Archives", "05_Journal", "06_Assets", "07_Temp" ) | ForEach-Object {
    New-Item -Path (Join-Path $RootPath $_) -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
}

Adding New Source Folders

Process additional source directories:

powershell
Get-ChildItem -Path (Join-Path $RootPath "YourSourceFolder") -Recurse -File | ForEach-Object {
    $relativePath = $_.FullName.Substring($RootPath.Length + 1)
    # Add your routing logic here
}

Version History

VersionDateChanges
PERFECTJune 2025Final version with June folder bugfix
v2EarlierIntermediate version

Use Cases

  1. Personal Knowledge Management: Reorganize Obsidian/Notion/OneNote exports
  2. Project Cleanup: Consolidate scattered project files
  3. Migration: Move files from old structure to new
  4. Archive Organization: Sort through historical files
  5. Client Files: Categorize client deliverables

Limitations

  • Windows Only: Uses PowerShell and Windows-specific cmdlets
  • Specific Structure: Designed for "Note Nest" pattern, needs adaptation for other structures
  • No Undo: Moves files permanently (can restore from log if needed)
  • Pattern Matching: Some files may need manual sorting to inbox

Security Considerations

  • Execution Policy: Requires RemoteSigned policy (safe default)
  • No Network Operations: Runs entirely local
  • Log Review: Always check log after running
  • Test First: Run on copy of data before original

Example Workflow

  1. Backup: Copy the folder to reorganize
  2. Test: Run script on copy
  3. Review: Check log file and 00_Inbox
  4. Adjust: Modify patterns if needed
  5. Apply: Run on original data

Detailed Implementation Analysis

Core Function: Move-ItemSafely

This is the workhorse function that handles all file moves with safety checks:

powershell
function Move-ItemSafely {
    param (
        [string]$RelativeSourcePath, 
        [string]$DestinationFolder, 
        [string]$NewName = ''
    )
    
    # Construct full paths
    $SourceFullPath = Join-Path $RootPath $RelativeSourcePath
    $DestFullPath = Join-Path $RootPath $DestinationFolder
    
    # Step 1: Check if source exists
    if (-not (Test-Path $SourceFullPath)) { 
        return  # Silent return for non-existent files
    }
    
    # Step 2: Create destination if needed
    if (-not (Test-Path $DestFullPath)) { 
        New-Item -Path $DestFullPath -ItemType Directory -Force | Out-Null 
    }
    
    # Step 3: Determine final filename
    $FinalDest = if ($NewName) { 
        Join-Path $DestFullPath $NewName 
    } else { 
        Join-Path $DestFullPath (Split-Path $SourceFullPath -Leaf) 
    }
    
    # Step 4: Attempt move with error handling
    try {
        Move-Item -LiteralPath $SourceFullPath -Destination $FinalDest -Force -ErrorAction Stop
        Add-Content -Path $LogFile -Value "MOVED: $RelativeSourcePath `n   TO: $FinalDest"
    } catch {
        Add-Content -Path $LogFile -Value "ERROR moving $RelativeSourcePath : $($_.Exception.Message)"
    }
}

Why this approach?

  1. Relative paths: Prevents typos from moving wrong files
  2. Silent return on missing source: Handles gracefully when file already moved
  3. Auto-create destinations: No need to pre-create folder structure
  4. -Force flag: Overwrites existing files if name collision
  5. -LiteralPath: Handles special characters in paths (spaces, brackets)
  6. Try-catch: Captures and logs errors without stopping script

Processing Pipeline

The script processes files in a specific order to handle dependencies:

Parsing system architecture diagram...

Phase 1: Create Folder Structure

powershell
# Create new structure
@( "00_Inbox", "01_Projects", "02_Areas", "03_Resources", "04_Archives", "05_Journal", "06_Assets", "07_Temp" ) | ForEach-Object {
    New-Item -Path (Join-Path $RootPath $_) -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
}

Phase 2: Pre-Process Special Folders

Handle folders that need special attention before general processing:

powershell
# Special folder: Site Structure
Move-ItemSafely -RelativeSourcePath "Note Nest\Important\Namani Construction\Site Structure" -DestinationFolder "01_Projects\Namani_Construction\Project_Documentation"

Phase 3: Process Collections

The Collections folder often contains mixed files:

powershell
Get-ChildItem -Path (Join-Path $RootPath "Collections") -Recurse -File | ForEach-Object {
    $relativePath = $_.FullName.Substring($RootPath.Length + 1)
    switch -Wildcard ($_.Name) {
        # HTML files → Website Development
        'About Us.html'         { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\Namani_Construction\Website_Development" }
        'blog.html'             { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\Namani_Construction\Website_Development" }
        
        # Brand files → Client Project
        'ASKBASKET Brand Guidelines.docx' { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\AskBasket" }
        
        # PDFs → Resources
        'Flexible Website*.pdf' { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "03_Resources\Pricing_Frameworks" }
        
        # Unknown → Inbox
        default { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "00_Inbox" }
    }
}

Phase 4: Process Note Nest Inner

The main note folder with many subfolders:

powershell
Get-ChildItem -Path (Join-Path $RootPath "Note Nest") -Recurse -File | ForEach-Object {
    # Skip already-processed special folders
    if ($relativePath -like '*\Site Structure\*' -or $relativePath -like '*\June\*') { return }
    
    # Pattern matching for routing
    switch -Wildcard ($_.Name) {
        'AI Learning Roadmap*' { Move-ItemSafely ... -DestinationFolder "02_Areas\AI_Automation" }
        'Pasted image*.png'   { Move-ItemSafely ... -DestinationFolder "06_Assets\Images" }
    }
}

Phase 5: Cleanup

Remove empty source directories:

powershell
@( "Collections", "Note Nest", "Prompt Wrapper", "Scripts" ) | ForEach-Object {
    $dirPath = Join-Path $RootPath $_
    if (Test-Path $dirPath) {
        try { Remove-Item -Path $dirPath -Recurse -Force -ErrorAction Stop } catch {}
    }
}

Date-Based Folder Processing

One of the most complex parts is handling date-based folders:

powershell
$juneRoot = Join-Path $RootPath "Note Nest\June"
if (Test-Path $juneRoot) {
    # Get all subdirectories (each is a date folder like "30-06-2024")
    Get-ChildItem -Path $juneRoot -Directory | ForEach-Object {
        $dateFolder = $_
        
        # Extract date components from folder name
        if ($dateFolder.Name -match '(\d{2})-(\d{2})-(\d{4})') {
            $year = $Matches[3]    # 2024
            $month = $Matches[2]   # 06
            $day = $Matches[1]     # 30
            
            # Convert month number to name
            $monthName = (Get-Culture).DateTimeFormat.GetMonthName([int]$month)
            
            # Determine destination: 05_Journal\2024\06_June
            $destJournalFolder = "05_Journal\$year\$($month)_$monthName"
            
            # Get all files in this date folder
            Get-ChildItem -Path $dateFolder.FullName -File | ForEach-Object {
                $file = $_
                $fileRelativePath = $file.FullName.Substring($RootPath.Length + 1)
                
                # Create new standardized name: "2024-06-30_Daily_Log_OriginalName"
                $newLogFileName = "$($year)-$($month)-$($day)_Daily_Log_$($file.Name)"
                
                Move-ItemSafely -RelativeSourcePath $fileRelativePath `
                                -DestinationFolder $destJournalFolder `
                                -NewName $newLogFileName
            }
        }
    }
}

Input → Output transformation:

code
Note Nest\June\30-06-2024\Daily Log.md
    → 05_Journal\2024\06_June\30-06-2024_Daily_Log_Daily Log.md

Conditional Routing

Some files need conditional logic based on their location:

powershell
'Website.md' { 
    # Only move if it's in an Aatmanova folder
    if ($_.DirectoryName -like '*Aatmanova*') { 
        Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\Aatmanova_Website" 
    } 
}

'fortress*.sh' { 
    # Only move if parent folder is "Secure VPS"
    if ($_.Directory.Name -eq 'Secure VPS') { 
        Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "01_Projects\VPS_Fortress_Development" 
    } 
}

Error Handling Strategy

The script uses multiple layers of error handling:

  1. Function-level: Move-ItemSafely has try-catch
  2. Folder-level: Silently continue if folder doesn't exist
  3. Removal-level: Silently continue if can't remove folder
  4. Logging: All errors written to log file
powershell
# Safe folder creation (non-terminating errors)
New-Item -Path (Join-Path $RootPath $_) -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null

# Safe folder removal
try { Remove-Item -Path $dirPath -Recurse -Force -ErrorAction Stop } catch {}

Performance Considerations

For large folder structures, consider these optimizations:

powershell
# Use -Recurse carefully
Get-ChildItem -Path (Join-Path $RootPath "Collections") -Recurse -File

# Path construction is O(1) but lots of them add up
# Using Substring is faster than repeated Join-Path

# Parallel processing (PowerShell 7+)
Get-ChildItem ... -File | ForEach-Object -Parallel {
    Move-ItemSafely ...
} -ThrottleLimit 10

Common Issues and Solutions

IssueCauseSolution
Files not movingSource path typoCheck log for errors
Destination not createdPermissionsRun as Administrator
Special characters fail-Path instead of -LiteralPathScript uses -LiteralPath
Script hangsInfinite loop in date parsingCheck folder name regex
Partial completionError in middleCheck log, re-run

Adapting for Different Structures

Example: Adding a New Category

To add a "Music" category:

powershell
# 1. Add folder to structure
@( "00_Inbox", "01_Projects", "02_Areas", "03_Resources", "04_Archives", "05_Journal", "06_Assets", "07_Temp", "08_Music" )

# 2. Add routing rules
'*.mp3' { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "08_Music" }
'*.wav' { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "08_Music" }
'Music*.md' { Move-ItemSafely -RelativeSourcePath $relativePath -DestinationFolder "08_Music" }

Example: Different Date Format

If your dates are in different format (e.g., "2024-06-30"):

powershell
# Update regex pattern
if ($dateFolder.Name -match '(\d{4})-(\d{2})-(\d{2})') {
    $year = $Matches[1]
    $month = $Matches[2]
    $day = $Matches[3]
    # ... rest of logic
}

Comparison with Alternatives

MethodProsCons
This ScriptFree, customizable, localWindows only, manual setup
Bulk Rename UtilityGUI, no codingLimited pattern matching
PowerAutomateNo code, cloud syncRequires Microsoft account
Python scriptCross-platformRequires Python installation
FileExplorerNative, no setupManual, no automation

Conclusion

This PowerShell folder organizer demonstrates:

  • Robust error handling with logging
  • Pattern-based routing for automatic categorization
  • Safe move operations that preserve data
  • Cleanup automation for removing empty folders
  • Customizability for different folder structures

It can be adapted for any reorganization task by modifying the switch patterns and folder structure definitions.

Architecture Feedback

Spotted a potential optimization or antipattern? Let me know.

Submit a Technical Suggestion