Public/Group-File.ps1
|
function Group-File { <# .SYNOPSIS Groups files into directories based on extension rules defined in a JSON config. .DESCRIPTION Reads GroupFile.config.json from the user's config directory to map file extensions to target directories. Files are moved to the first existing allowed directory. If -Force is used, missing directories are created and existing files are overwritten. .PARAMETER Path Target directory to process. Defaults to current directory. .PARAMETER Only Processes only rules containing at least one of the specified extensions. .PARAMETER Force Creates missing target directories and overwrites existing destination files. .PARAMETER Config Opens the directory containing the configuration file for editing. .NOTES On first run, the module creates a default config and opens it for editing. Run the command again to actually organize your files. #> [CmdletBinding()] param ( [Parameter(Position = 0)] [ValidateScript({ Test-Path $_ -PathType Container })] [string]$Path = ".", [string[]]$Only, [switch]$Force, [switch]$OpenConfig ) if ($OpenConfig) { $configDir = Get-GroupFileConfigDir if (-not (Test-Path -Path $configDir)) { New-Item -Path $configDir -ItemType Directory -Force | Out-Null } Invoke-Item -Path $configDir return } Write-Verbose "Starting file grouping in: $Path" # 1. Load config $config = Get-GroupFileConfig if ($null -eq $config) { return } # 2. Gather files and directories $allFiles = Get-ChildItem -Path $Path -File $allDirs = @(Get-ChildItem -Path $Path -Directory | Where-Object { -not $_.Name.StartsWith('.') }) if ($allFiles.Count -eq 0) { Write-Verbose "No files found in $Path. Exiting." return } # 3. Group files by extension $filesByExtension = Get-FilesGroupedByExtension -Files $allFiles # Normalize -Only parameter $normalizedOnly = if ($Only) { $Only | ForEach-Object { $_.ToLower() } } else { $null } # 4. Process rules foreach ($rule in $config.Rules) { $ruleExts = $rule.Extensions | ForEach-Object { $_.ToLower() } # Skip rule if -Only is used and doesn't match if ($normalizedOnly) { $hasMatch = $false foreach ($ext in $ruleExts) { if ($ext -in $normalizedOnly) { $hasMatch = $true break } } if (-not $hasMatch) { continue } } # Get target files for this rule $targetFiles = [System.Collections.Generic.List[object]]::new() foreach ($ext in $ruleExts) { if ($filesByExtension.ContainsKey($ext)) { $targetFiles.AddRange($filesByExtension[$ext]) } } if ($targetFiles.Count -eq 0) { continue } # Find or create target directory $targetDir = Find-TargetDirectory -AllowedDirectories $rule.AllowedDirectories -AllDirectories $allDirs if (-not $targetDir) { $targetDir = New-TargetDirectoryIfNeeded -Path $Path -DefaultDirectory $rule.DefaultDirectory -Force:$Force -AllDirectories ([ref]$allDirs) } if (-not $targetDir) { Write-Warning "No target directory found for [$($rule.Extensions -join ', ')] and -Force is not specified. Skipping $($targetFiles.Count) files." continue } # Move files Write-Verbose "Processing $($targetFiles.Count) file(s) to '$($targetDir.Name)'." Move-FilesToDirectory -Files $targetFiles -TargetDirectory $targetDir -Force:$Force } Write-Verbose "Finish sort file(s)" } |