<# .SYNOPSIS Automates the setup of uutils/coreutils on Windows by creating batch file shims. #> [CmdletBinding(SupportsShouldProcess = $true)] param( [string]$ShimsDirectory = (Join-Path $env:LOCALAPPDATA "uutils-shims"), [switch]$ForceInstall, [switch]$ForcePathAddition, [switch]$SkipPathCheck ) $ErrorActionPreference = "Stop" function Show-Menu ([string]$Title, [string]$PromptMessage) { Write-Host "`n--- $Title ---" $options = @( [System.Management.Automation.Host.ChoiceDescription]::new("&Yes", "Proceed.") [System.Management.Automation.Host.ChoiceDescription]::new("&No", "Skip.") ) return (0 -eq $host.UI.PromptForChoice($Title, $PromptMessage, $options, 0)) } function Find-CoreutilsExecutable { Write-Verbose "Searching for coreutils.exe..." $coreutilsCmd = Get-Command coreutils -ErrorAction SilentlyContinue if ($coreutilsCmd -and (Test-Path $coreutilsCmd.Source)) { return $coreutilsCmd.Source } $wingetBase = Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Packages" $pkgId = "uutils.coreutils_Microsoft.Winget.Source_8wekyb3d8bbwe" if (Test-Path $wingetBase) { $path = Get-ChildItem (Join-Path $wingetBase $pkgId) -Recurse -Filter "coreutils.exe" | Sort-Object LastWriteTime -Descending | Select-Object -ExpandProperty FullName -First 1 if ($path) { return $path } } if ($ForceInstall -or (Show-Menu "Install Coreutils" "coreutils.exe not found. Install via winget?")) { Write-Host "Installing uutils via winget..." -ForegroundColor Cyan Start-Process winget -ArgumentList "install uutils.coreutils --source winget -e --accept-package-agreements" -Wait return Get-ChildItem (Join-Path $wingetBase $pkgId) -Recurse -Filter "coreutils.exe" | Select-Object -ExpandProperty FullName -First 1 } return $null } function Get-CoreutilsCommands ([string]$CoreutilsExePath) { Write-Verbose "Parsing commands from $CoreutilsExePath" $raw = (& $CoreutilsExePath --list | Out-String) if ($raw -match "Currently defined functions:") { $clean = $raw -split "Currently defined functions:" | Select-Object -Last 1 $commands = $clean.Replace("[", "").Replace("]", "").Replace("`n", " ").Replace("`r", " ") -split '[, ]+' | Where-Object { $_.Trim() -ne "" } return $commands } return $raw -split '\s+' | Where-Object { $_ -ne "" } } function New-UtilityShim { [CmdletBinding(SupportsShouldProcess = $true)] param([string]$CoreutilsExePath, [array]$Commands, [string]$TargetDir) if (-not (Test-Path $TargetDir)) { if ($PSCmdlet.ShouldProcess($TargetDir, "Create Directory")) { New-Item -Path $TargetDir -ItemType Directory -Force | Out-Null } } $created = 0 foreach ($cmd in $Commands) { $fileName = if ($cmd -eq "[") { "bracket.bat" } else { "$cmd.bat" } $path = Join-Path $TargetDir $fileName $content = "@echo off`n`"$CoreutilsExePath`" $cmd %*" if ($PSCmdlet.ShouldProcess("Shim for $cmd", "Create batch file at $path")) { Set-Content -Path $path -Value $content -Encoding Ascii $created++ } } Write-Host "Created $created shims in $TargetDir" -ForegroundColor Cyan } function Update-UserPath { [CmdletBinding(SupportsShouldProcess = $true)] param([string]$Dir, [switch]$ForcePathAddition) $currentPath = [System.Environment]::GetEnvironmentVariable("Path", "User") if ($currentPath -split ';' -contains $Dir) { Write-Host "Directory already in User PATH." -ForegroundColor Green return } if ($ForcePathAddition -or (Show-Menu "Update PATH" "Add $Dir to User PATH?")) { $newPath = "$currentPath;$Dir".Trim(';') if ($PSCmdlet.ShouldProcess("User PATH Environment Variable", "Append $Dir")) { [System.Environment]::SetEnvironmentVariable("Path", $newPath, "User") Write-Host "PATH updated. Restart your terminal to apply changes." -ForegroundColor Yellow } } } Write-Host "--- uutils setup ---" -ForegroundColor Magenta $exe = Find-CoreutilsExecutable if ($exe) { $cmds = Get-CoreutilsCommands -CoreutilsExePath $exe New-UtilityShim -CoreutilsExePath $exe -Commands $cmds -TargetDir $ShimsDirectory if (-not $SkipPathCheck) { Update-UserPath -Dir $ShimsDirectory -ForcePathAddition:$ForcePathAddition } } else { Write-Error "Coreutils not found." }