Last active 1 month ago

Setup-CoreutilsLinks.ps1 Raw
<#
.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."
}