Initial commit
This commit is contained in:
commit
71b1c70596
1 changed files with 389 additions and 0 deletions
389
organise-isos.ps1
Normal file
389
organise-isos.ps1
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
# organise-isos.ps1
|
||||
# Usage: .\organise-isos.ps1 [-SourceDir <path>] [-DestDir <path>] [-DryRun]
|
||||
|
||||
param(
|
||||
[string]$SourceDir = ".",
|
||||
[string]$DestDir = ".\iso-library",
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
# ── Arch detection ─────────────────────────────────────────────────────────────
|
||||
$archPatterns = @(
|
||||
@{ Pattern = 'aarch64'; Canonical = 'aarch64' },
|
||||
@{ Pattern = 'arm64'; Canonical = 'aarch64' },
|
||||
@{ Pattern = 'arm(?!64)'; Canonical = 'arm' },
|
||||
@{ Pattern = 'x86[_-]64'; Canonical = 'x86_64' },
|
||||
@{ Pattern = 'amd64'; Canonical = 'x86_64' },
|
||||
@{ Pattern = '_x64(?=[_\.\-]|$)'; Canonical = 'x86_64' }, # Win11_x64
|
||||
@{ Pattern = '64bit'; Canonical = 'x86_64' },
|
||||
@{ Pattern = 'x86(?![_-]64)'; Canonical = 'x86' },
|
||||
@{ Pattern = 'i[3-6]86'; Canonical = 'x86' },
|
||||
@{ Pattern = '32bit'; Canonical = 'x86' },
|
||||
@{ Pattern = 'riscv64'; Canonical = 'riscv64' },
|
||||
@{ Pattern = 'riscv32'; Canonical = 'riscv32' },
|
||||
@{ Pattern = 'ppc64le'; Canonical = 'ppc64le' },
|
||||
@{ Pattern = 'ppc64'; Canonical = 'ppc64' },
|
||||
@{ Pattern = 'ppc'; Canonical = 'ppc' },
|
||||
@{ Pattern = 's390x'; Canonical = 's390x' },
|
||||
@{ Pattern = 'mips64'; Canonical = 'mips64' },
|
||||
@{ Pattern = 'mips(?!64)'; Canonical = 'mips' },
|
||||
@{ Pattern = 'loong64'; Canonical = 'loong64' },
|
||||
@{ Pattern = 'loongarch64'; Canonical = 'loong64' },
|
||||
@{ Pattern = 'ia64'; Canonical = 'ia64' },
|
||||
@{ Pattern = 'itanium'; Canonical = 'ia64' },
|
||||
@{ Pattern = 'sparc64'; Canonical = 'sparc64' },
|
||||
@{ Pattern = 'alpha'; Canonical = 'alpha' }
|
||||
)
|
||||
|
||||
# ── Edition/noise tokens to strip ──────────────────────────────────────────────
|
||||
# NOTE: deliberately does NOT include 'memtest', 'rescue', 'live' as standalone
|
||||
# OS names — those are caught by the OS map first, stripping only runs after
|
||||
# the first-token OS match.
|
||||
$noisePattern = '(?i)\b(live|server|desktop|dvd|netinstall|netinst|installer|' +
|
||||
'everything|boot|minimal|workstation|kde|gnome|xfce|lxde|mate|cinnamon|' +
|
||||
'budgie|standard|full|net|bd|cd|usb|dvd1|dvd2|disc\d*|disk\d*|' +
|
||||
'multiarch|hybrid|recovery|update|enterprise|community|' +
|
||||
'education|educational|studio|gaming|security|nonfree|non-free|' +
|
||||
'free|oss|lts|eol|stable|testing|sid|snapshot|nightly|beta|alpha|' +
|
||||
'rc\d*|sp\d*|u\d+|patch|hotfix|errata|image|' +
|
||||
'english|international|intl|en_us|en_gb)\b'
|
||||
|
||||
# ── OS normalisation map ────────────────────────────────────────────────────────
|
||||
$osNormMap = [ordered]@{
|
||||
|
||||
# ── Windows family ──────────────────────────────────────────────────────
|
||||
'^win(dows)?(svr|server)(2025|2022|2019|2016|2012|2008|2003)' = 'windows-server'
|
||||
'^win(dows)?(svr|server)' = 'windows-server'
|
||||
'^win(dows)?' = 'windows'
|
||||
'^ms.?dos|^msdos' = 'ms-dos'
|
||||
'^reactos' = 'reactos'
|
||||
|
||||
# ── RHEL family ─────────────────────────────────────────────────────────
|
||||
'^rhel|^redhat|^red.hat' = 'rhel'
|
||||
'^centos.stream' = 'centos-stream'
|
||||
'^centos' = 'centos'
|
||||
'^rocky' = 'rocky'
|
||||
'^alma' = 'almalinux'
|
||||
'^oracle.linux|^oraclelinux' = 'oraclelinux'
|
||||
'^scientific' = 'scientific'
|
||||
'^eurolinux' = 'eurolinux'
|
||||
'^springdale' = 'springdale'
|
||||
'^clearos' = 'clearos'
|
||||
'^navylinux|^navy.linux' = 'navylinux'
|
||||
|
||||
# ── Fedora family ───────────────────────────────────────────────────────
|
||||
'^fedora' = 'fedora'
|
||||
'^qubes' = 'qubes'
|
||||
'^korora' = 'korora'
|
||||
|
||||
# ── SUSE family ─────────────────────────────────────────────────────────
|
||||
'^opensuse.leap' = 'opensuse-leap'
|
||||
'^opensuse.tumbleweed' = 'opensuse-tumbleweed'
|
||||
'^opensuse.microos' = 'opensuse-microos'
|
||||
'^opensuse.kubic' = 'opensuse-kubic'
|
||||
'^opensuse' = 'opensuse'
|
||||
'^sles|^suse.linux.enterprise' = 'sles'
|
||||
'^suse' = 'opensuse'
|
||||
'^geckolinux' = 'geckolinux'
|
||||
|
||||
# ── Debian family ───────────────────────────────────────────────────────
|
||||
'^debian' = 'debian'
|
||||
'^ubuntu.mate' = 'ubuntu-mate'
|
||||
'^ubuntu.budgie' = 'ubuntu-budgie'
|
||||
'^ubuntu.kylin' = 'ubuntu-kylin'
|
||||
'^ubuntu.studio' = 'ubuntu-studio'
|
||||
'^ubuntu.unity' = 'ubuntu-unity'
|
||||
'^lubuntu' = 'lubuntu'
|
||||
'^kubuntu' = 'kubuntu'
|
||||
'^xubuntu' = 'xubuntu'
|
||||
'^ubuntu' = 'ubuntu'
|
||||
'^linuxmint|^linux.mint|^mint' = 'linuxmint'
|
||||
'^lmde' = 'lmde'
|
||||
'^pop.os|^popos' = 'pop_os'
|
||||
'^elementary' = 'elementary'
|
||||
'^zorin' = 'zorin'
|
||||
'^kali' = 'kali'
|
||||
'^parrot' = 'parrot'
|
||||
'^backbox' = 'backbox'
|
||||
'^tails' = 'tails'
|
||||
'^whonix' = 'whonix'
|
||||
'^raspbian|^raspberry.pi.os' = 'raspios'
|
||||
'^armbian' = 'armbian'
|
||||
'^devuan' = 'devuan'
|
||||
'^mx.linux|^mxlinux' = 'mx'
|
||||
'^antix' = 'antix'
|
||||
'^deepin' = 'deepin'
|
||||
'^pureos' = 'pureos'
|
||||
'^trisquel' = 'trisquel'
|
||||
'^siduction' = 'siduction'
|
||||
'^sparky' = 'sparkylinux'
|
||||
'^bunsenlabs|^bunsen' = 'bunsenlabs'
|
||||
'^crunchbang' = 'crunchbangpp'
|
||||
'^bodhi' = 'bodhi'
|
||||
'^q4os' = 'q4os'
|
||||
'^netrunner' = 'netrunner'
|
||||
'^kaisen' = 'kaisen'
|
||||
'^grml' = 'grml'
|
||||
'^libreelec' = 'libreelec'
|
||||
'^osmc' = 'osmc'
|
||||
|
||||
# ── Arch family ─────────────────────────────────────────────────────────
|
||||
'^archlinux|^arch.linux' = 'arch'
|
||||
'^arch(?!ive)' = 'arch'
|
||||
'^manjaro' = 'manjaro'
|
||||
'^endeavouros|^endeavour.os' = 'endeavouros'
|
||||
'^garuda' = 'garuda'
|
||||
'^artix' = 'artix'
|
||||
'^parabola' = 'parabola'
|
||||
'^hyperbola' = 'hyperbola'
|
||||
'^blackarch' = 'blackarch'
|
||||
'^archcraft' = 'archcraft'
|
||||
'^arcolinux' = 'arcolinux'
|
||||
'^cachyos' = 'cachyos'
|
||||
'^crystal.linux' = 'crystallinux'
|
||||
'^blendos' = 'blendos'
|
||||
|
||||
# ── Gentoo family ───────────────────────────────────────────────────────
|
||||
'^gentoo' = 'gentoo'
|
||||
'^calculate' = 'calculate'
|
||||
'^sabayon' = 'sabayon'
|
||||
'^funtoo' = 'funtoo'
|
||||
'^chromeos|^chrome.os' = 'chromeos'
|
||||
'^chromiumos|^chromium.os' = 'chromiumos'
|
||||
'^fydeos' = 'fydeos'
|
||||
|
||||
# ── Slackware family ────────────────────────────────────────────────────
|
||||
'^slackware' = 'slackware'
|
||||
'^salix' = 'salix'
|
||||
'^porteus' = 'porteus'
|
||||
'^slax' = 'slax'
|
||||
'^zenwalk' = 'zenwalk'
|
||||
|
||||
# ── Independent / other Linux ────────────────────────────────────────────
|
||||
'^void' = 'void'
|
||||
'^nixos|^nix(?=\d)' = 'nixos'
|
||||
'^guix' = 'guix'
|
||||
'^alpine' = 'alpine'
|
||||
'^tinycore|^tiny.core' = 'tinycore'
|
||||
'^puppy' = 'puppy'
|
||||
'^slitaz' = 'slitaz'
|
||||
'^dsl.linux|^damn.small' = 'dsl'
|
||||
'^knoppix' = 'knoppix'
|
||||
'^pclinuxos' = 'pclinuxos'
|
||||
'^mageia' = 'mageia'
|
||||
'^mandriva|^mandrake' = 'mandriva'
|
||||
'^openmandriva' = 'openmandriva'
|
||||
'^solus' = 'solus'
|
||||
'^clear.linux' = 'clearlinux'
|
||||
'^photon' = 'photon'
|
||||
'^flatcar' = 'flatcar'
|
||||
'^coreos|^fedora.coreos' = 'coreos'
|
||||
'^bottlerocket' = 'bottlerocket'
|
||||
'^talos' = 'talos'
|
||||
'^kairos' = 'kairos'
|
||||
'^rancheros' = 'rancheros'
|
||||
'^rancher' = 'rancher'
|
||||
'^k3os' = 'k3os'
|
||||
'^openwrt' = 'openwrt'
|
||||
'^ddwrt|^dd.wrt' = 'ddwrt'
|
||||
'^pfsense' = 'pfsense'
|
||||
'^opnsense' = 'opnsense'
|
||||
'^ipfire' = 'ipfire'
|
||||
'^endian' = 'endian'
|
||||
'^untangle' = 'untangle'
|
||||
'^smoothwall' = 'smoothwall'
|
||||
'^zeroshell' = 'zeroshell'
|
||||
'^vyos' = 'vyos'
|
||||
'^astralinux|^astr.linux' = 'astralinux'
|
||||
'^altlinux|^alt.linux' = 'altlinux'
|
||||
'^redos|^red.os' = 'redos'
|
||||
'^elbrus' = 'elbrus'
|
||||
'^rosa' = 'rosa'
|
||||
|
||||
# ── Hypervisor / virtualisation ──────────────────────────────────────────
|
||||
'^proxmox.ve|^pve' = 'proxmox-ve'
|
||||
'^proxmox.backup|^pbs' = 'proxmox-bs'
|
||||
'^proxmox.mail|^pmg' = 'proxmox-mg'
|
||||
'^proxmox' = 'proxmox-ve'
|
||||
'^esxi|^vmware.esxi' = 'esxi'
|
||||
'^vsphere' = 'vsphere'
|
||||
'^vmware' = 'vmware'
|
||||
'^xenserver|^citrix.hypervisor' = 'xenserver'
|
||||
'^xcp.ng' = 'xcp-ng'
|
||||
'^harvester' = 'harvester'
|
||||
'^nutanix' = 'nutanix'
|
||||
|
||||
# ── BSD family ──────────────────────────────────────────────────────────
|
||||
'^freebsd' = 'freebsd'
|
||||
'^openbsd' = 'openbsd'
|
||||
'^netbsd' = 'netbsd'
|
||||
'^dragonfly.bsd' = 'dragonflybsd'
|
||||
'^truenas.scale' = 'truenas-scale'
|
||||
'^truenas.core|^freenas' = 'truenas-core'
|
||||
'^truenas' = 'truenas'
|
||||
'^ghostbsd' = 'ghostbsd'
|
||||
'^midnightbsd' = 'midnightbsd'
|
||||
'^hardenedbsd' = 'hardenedbsd'
|
||||
'^nomadbsd' = 'nomadbsd'
|
||||
|
||||
# ── macOS / Darwin ───────────────────────────────────────────────────────
|
||||
'^macos|^mac.os|^osx' = 'macos'
|
||||
'^darwin' = 'darwin'
|
||||
|
||||
# ── Solaris / illumos ────────────────────────────────────────────────────
|
||||
'^opensolaris' = 'opensolaris'
|
||||
'^openindiana' = 'openindiana'
|
||||
'^omnios' = 'omnios'
|
||||
'^tribblix' = 'tribblix'
|
||||
'^solaris' = 'solaris'
|
||||
'^smartos' = 'smartos'
|
||||
|
||||
# ── Other Unix / exotic ──────────────────────────────────────────────────
|
||||
'^haiku' = 'haiku'
|
||||
'^freedos' = 'freedos'
|
||||
'^plan9' = 'plan9'
|
||||
'^minix' = 'minix'
|
||||
'^morphos' = 'morphos'
|
||||
'^amigaos' = 'amigaos'
|
||||
'^aros' = 'aros'
|
||||
'^serenityos' = 'serenityos'
|
||||
'^redox' = 'redox'
|
||||
'^kolibrios' = 'kolibrios'
|
||||
'^menuetos' = 'menuetos'
|
||||
'^syllable' = 'syllable'
|
||||
'^genode' = 'genode'
|
||||
'^fuchsia' = 'fuchsia'
|
||||
|
||||
# ── Rescue / tools ───────────────────────────────────────────────────────
|
||||
'^clonezilla' = 'clonezilla'
|
||||
'^systemrescue|^system.rescue' = 'systemrescue'
|
||||
'^gparted' = 'gparted'
|
||||
'^partedmagic' = 'partedmagic'
|
||||
'^hirens|^hiren' = 'hirens'
|
||||
'^ubcd|^ultimate.boot' = 'ubcd'
|
||||
'^memtest86' = 'memtest86'
|
||||
'^memtest' = 'memtest'
|
||||
'^dban' = 'dban'
|
||||
'^rescuezilla' = 'rescuezilla'
|
||||
'^winpe' = 'winpe'
|
||||
'^ipxe' = 'ipxe'
|
||||
'^kaspersky' = 'kaspersky-rescue'
|
||||
'^bitdefender' = 'bitdefender-rescue'
|
||||
'^drweb' = 'drweb-rescue'
|
||||
'^eset' = 'eset-rescue'
|
||||
}
|
||||
|
||||
function Get-IsoMeta {
|
||||
param([string]$Filename)
|
||||
|
||||
$base = [System.IO.Path]::GetFileNameWithoutExtension($Filename)
|
||||
|
||||
# ── Arch (raw filename before any stripping) ───────────────────────────
|
||||
$arch = 'noarch'
|
||||
foreach ($ap in $archPatterns) {
|
||||
if ($base -match "(?i)$($ap.Pattern)") {
|
||||
$arch = $ap.Canonical
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# ── Strip arch tokens and edition noise ────────────────────────────────
|
||||
$clean = $base
|
||||
foreach ($ap in $archPatterns) {
|
||||
$clean = $clean -replace "(?i)$($ap.Pattern)", ''
|
||||
}
|
||||
$clean = $clean -replace $noisePattern, ''
|
||||
$clean = $clean -replace '[-_\.]{2,}', '-'
|
||||
$clean = $clean.Trim('-_. ')
|
||||
|
||||
# ── OS: split and take first token, cast to string explicitly ─────────
|
||||
$segments = @($clean -split '[-_\.]' | Where-Object { $_ -ne '' })
|
||||
$firstToken = if ($segments.Count -gt 0) { [string]$segments[0] } else { 'unknown' }
|
||||
$firstToken = $firstToken.ToLower()
|
||||
|
||||
$os = 'unknown'
|
||||
foreach ($pattern in $osNormMap.Keys) {
|
||||
if ($firstToken -match $pattern) {
|
||||
$os = $osNormMap[$pattern]
|
||||
break
|
||||
}
|
||||
}
|
||||
# Try first two tokens joined — catches "linux-mint", "pop-os" etc.
|
||||
if ($os -eq 'unknown' -and $segments.Count -ge 2) {
|
||||
$twoToken = ([string]$segments[0] + '-' + [string]$segments[1]).ToLower()
|
||||
foreach ($pattern in $osNormMap.Keys) {
|
||||
if ($twoToken -match $pattern) {
|
||||
$os = $osNormMap[$pattern]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($os -eq 'unknown') {
|
||||
$os = $firstToken -replace '[^a-z0-9\-]', ''
|
||||
if (-not $os) { $os = 'unknown' }
|
||||
}
|
||||
|
||||
# ── Version ───────────────────────────────────────────────────────────
|
||||
# Note: version parsing uses $base (raw) for date stamps, $clean for
|
||||
# everything else, to avoid stripping corrupting the match.
|
||||
$version = 'unknown'
|
||||
|
||||
# Windows H-series (25H2, 24H2 etc.) — run on raw base
|
||||
if ($base -match '(?i)\b(\d{2}H[12])\b') {
|
||||
$version = $matches[1].ToUpper()
|
||||
}
|
||||
# Full date stamp YYYYMMDD — extract just the year as version grouping
|
||||
elseif ($base -match '\b(20\d{2})(\d{2})(\d{2})\b') {
|
||||
$version = $matches[1]
|
||||
}
|
||||
# Semver / partial semver from cleaned string
|
||||
elseif ($clean -match '(?<![a-zA-Z])(\d+\.\d+(\.\d+)?)') {
|
||||
$parts = $matches[1] -split '\.'
|
||||
$version = if ($parts.Count -ge 3) { "$($parts[0]).$($parts[1])" } else { $matches[1] }
|
||||
}
|
||||
# Bare integer
|
||||
elseif ($clean -match '(?<![a-zA-Z])(\d{1,3})(?![a-zA-Z0-9\._])') {
|
||||
$version = $matches[1]
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{ OS = $os; Version = $version; Arch = $arch }
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
$isos = Get-ChildItem -Path $SourceDir -Filter "*.iso"
|
||||
|
||||
if ($isos.Count -eq 0) { Write-Error "No ISO files found in $SourceDir"; exit 1 }
|
||||
if ($DryRun) { Write-Host "[DRY RUN] No files will be moved`n" -ForegroundColor Yellow }
|
||||
|
||||
$results = @()
|
||||
foreach ($iso in $isos) {
|
||||
$meta = Get-IsoMeta $iso.Name
|
||||
$target = Join-Path $DestDir "$($meta.OS)\$($meta.Version)\$($meta.Arch)"
|
||||
|
||||
Write-Host $iso.Name -NoNewline
|
||||
Write-Host " -> " -NoNewline -ForegroundColor DarkGray
|
||||
Write-Host "$($meta.OS)\$($meta.Version)\$($meta.Arch)" -ForegroundColor Cyan
|
||||
|
||||
if (-not $DryRun) {
|
||||
New-Item -ItemType Directory -Path $target -Force | Out-Null
|
||||
Move-Item -Path $iso.FullName -Destination (Join-Path $target $iso.Name)
|
||||
}
|
||||
|
||||
$results += [PSCustomObject]@{
|
||||
File = $iso.Name
|
||||
OS = $meta.OS
|
||||
Version = $meta.Version
|
||||
Arch = $meta.Arch
|
||||
Target = "$($meta.OS)\$($meta.Version)\$($meta.Arch)"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
$results | Format-Table -AutoSize
|
||||
|
||||
if (-not $DryRun) {
|
||||
Write-Host "`nFinal structure:"
|
||||
Get-ChildItem -Path $DestDir -Recurse -Filter "*.iso" |
|
||||
ForEach-Object { $_.FullName.Replace("$((Resolve-Path $DestDir).Path)\", "") } |
|
||||
Sort-Object
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue