<# .SYNOPSIS Moves game files whose names contain unwanted keywords into a review folder, instead of deleting them. .DESCRIPTION Checks every file name against a list of keywords, like "(Beta)", "(Proto)", "Mahjong" or "(Germany)", and moves each match into a review folder, keeping its subfolder path so you can put anything back where it came from. Keywords are matched as plain text, ignoring case. Use -WholeWord so "Pool" doesn't catch "Liverpool", or -Regex to write your own patterns. Files matching an -Except pattern are never moved (for example "[!]", the verified-dump flag). Nothing is deleted. Once you've looked through the review folder, deleting it is up to you. Every match gets an object back with the keyword that caught it. Supports -WhatIf. .PARAMETER Path The folder to check. .PARAMETER Keyword Keywords to look for. Combine with -KeywordFile or use either one. .PARAMETER KeywordFile A text file with one keyword per line. Blank lines and lines starting with # are ignored. .PARAMETER ReviewFolder Where matches go. Default: a "_review" folder inside -Path. .PARAMETER Recurse Check subfolders too (the review folder itself is always skipped). .PARAMETER Extension Only check these extensions. Default: every file. .PARAMETER WholeWord Only match keywords as whole words. .PARAMETER Regex Treat keywords as regular expressions. .PARAMETER Except Regular expressions for names that must never be moved. .EXAMPLE .\Move-RomByKeyword.ps1 -Path D:\Games\snes -Keyword '(Beta)','(Proto)','(Demo)' -WhatIf .EXAMPLE .\Move-RomByKeyword.ps1 -Path D:\Games -Recurse -KeywordFile .\skip-list.txt -WholeWord -Except '\[!\]' | Export-Csv moved.csv -NoTypeInformation #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] [string]$Path, [string[]]$Keyword, [ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })] [string]$KeywordFile, [string]$ReviewFolder, [switch]$Recurse, [string[]]$Extension, [switch]$WholeWord, [switch]$Regex, [string[]]$Except ) $root = (Resolve-Path -LiteralPath $Path).ProviderPath.TrimEnd('\', '/') if (-not $ReviewFolder) { $ReviewFolder = Join-Path $root '_review' } $reviewFull = [System.IO.Path]::GetFullPath($ReviewFolder).TrimEnd('\', '/') $words = @($Keyword | Where-Object { $_ }) if ($KeywordFile) { $words += Get-Content -LiteralPath $KeywordFile | ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') } } if (-not $words.Count) { throw 'Give at least one keyword with -Keyword or -KeywordFile.' } $patterns = foreach ($w in $words) { $p = if ($Regex) { $w } else { [regex]::Escape($w) } if ($WholeWord) { $p = "(?