[CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true)] [string]$Command, [Parameter(Position = 1, ValueFromRemainingArguments = $true)] [string[]]$Arguments ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $Arguments = @($Arguments) function Resolve-MSOpenPath { param( [Parameter(Mandatory)] [string]$Path ) $resolved = Resolve-Path -LiteralPath $Path -ErrorAction Stop return $resolved.Path } function Invoke-Open { param([string[]]$Args) $target = if ($Args.Count -gt 0) { $Args[0] } else { (Get-Location).Path } if (Test-Path -LiteralPath $target) { $target = Resolve-MSOpenPath -Path $target } Start-Process -FilePath $target } function Invoke-Reveal { param([string[]]$Args) if ($Args.Count -lt 1) { throw 'Usage: reveal ' } $target = Resolve-MSOpenPath -Path $Args[0] if (Test-Path -LiteralPath $target -PathType Container) { Start-Process -FilePath 'explorer.exe' -ArgumentList "`"$target`"" return } Start-Process -FilePath 'explorer.exe' -ArgumentList "/select,`"$target`"" } function Invoke-CopyPath { param([string[]]$Args) $target = if ($Args.Count -gt 0) { Resolve-MSOpenPath -Path $Args[0] } else { (Get-Location).Path } Set-Clipboard -Value $target Write-Output $target } function Invoke-Paths { $entries = $env:Path -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ } $index = 1 foreach ($entry in $entries) { [pscustomobject]@{ Number = $index Path = $entry Exists = Test-Path -LiteralPath $entry } $index++ } } function Invoke-Which { param([string[]]$Args) if ($Args.Count -lt 1) { throw 'Usage: which ' } $commands = Get-Command -Name $Args[0] -All -ErrorAction Stop foreach ($item in $commands) { $location = $null if ($item.PSObject.Properties.Name -contains 'Path' -and $item.Path) { $location = $item.Path } elseif ($item.Source) { $location = $item.Source } elseif ($item.Definition) { $location = $item.Definition } [pscustomobject]@{ CommandType = $item.CommandType Name = $item.Name Location = $location } } } function Get-ListeningPorts { $connections = Get-NetTCPConnection -State Listen -ErrorAction Stop | Sort-Object LocalPort, LocalAddress foreach ($connection in $connections) { $processName = '' if ($connection.OwningProcess -eq 4) { $processName = 'System' } else { try { $processName = (Get-Process -Id $connection.OwningProcess -ErrorAction Stop).ProcessName } catch { $processName = '' } } [pscustomobject]@{ Protocol = 'TCP' Address = $connection.LocalAddress Port = $connection.LocalPort PID = $connection.OwningProcess Process = $processName } } } function Invoke-Ports { Get-ListeningPorts | Format-Table -AutoSize } function Invoke-KillPort { param([string[]]$Args) if ($Args.Count -lt 1) { throw 'Usage: killport [--force]' } $port = 0 if (-not [int]::TryParse($Args[0], [ref]$port) -or $port -lt 1 -or $port -gt 65535) { throw "Invalid port: $($Args[0])" } $force = $Args -contains '--force' $connections = Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue $processIds = @($connections | Select-Object -ExpandProperty OwningProcess -Unique) if ($processIds.Count -eq 0) { throw "Nothing is listening on port $port." } $targets = foreach ($processId in $processIds) { $name = '' try { $name = (Get-Process -Id $processId -ErrorAction Stop).ProcessName } catch { $name = '' } [pscustomobject]@{ Port = $port PID = $processId Process = $name } } $targets | Format-Table -AutoSize $protected = @($targets | Where-Object { $_.PID -in 0, 4 }) if ($protected.Count -gt 0) { throw 'Refusing to terminate a protected Windows system process.' } if (-not $force) { $answer = Read-Host "Terminate the process owning port $port? [y/N]" if ($answer -notmatch '^(y|yes)$') { Write-Host 'Cancelled.' return } } foreach ($target in $targets) { Stop-Process -Id $target.PID -Force -ErrorAction Stop Write-Host "Stopped $($target.Process) (PID $($target.PID))." } } function Invoke-Sha256 { param([string[]]$Args) if ($Args.Count -lt 1) { throw 'Usage: sha256 ' } $target = Resolve-MSOpenPath -Path $Args[0] if (-not (Test-Path -LiteralPath $target -PathType Leaf)) { throw "Not a file: $target" } $hash = Get-FileHash -LiteralPath $target -Algorithm SHA256 Write-Output "$($hash.Hash.ToLowerInvariant()) $target" } function Invoke-Pbcopy { param([string[]]$Args) $text = if ($Args.Count -gt 0) { $Args -join ' ' } elseif ([Console]::IsInputRedirected) { [Console]::In.ReadToEnd() } else { '' } Set-Clipboard -Value $text } function Invoke-Pbpaste { $value = Get-Clipboard -Raw if ($null -ne $value) { [Console]::Out.Write($value) } } function Get-MimeType { param([string]$Path) switch ([IO.Path]::GetExtension($Path).ToLowerInvariant()) { '.html' { 'text/html; charset=utf-8' } '.htm' { 'text/html; charset=utf-8' } '.css' { 'text/css; charset=utf-8' } '.js' { 'text/javascript; charset=utf-8' } '.json' { 'application/json; charset=utf-8' } '.xml' { 'application/xml; charset=utf-8' } '.txt' { 'text/plain; charset=utf-8' } '.md' { 'text/markdown; charset=utf-8' } '.svg' { 'image/svg+xml' } '.png' { 'image/png' } '.jpg' { 'image/jpeg' } '.jpeg' { 'image/jpeg' } '.gif' { 'image/gif' } '.webp' { 'image/webp' } '.ico' { 'image/x-icon' } '.pdf' { 'application/pdf' } '.wasm' { 'application/wasm' } '.zip' { 'application/zip' } default { 'application/octet-stream' } } } function Send-TextResponse { param( [Parameter(Mandatory)] [System.Net.HttpListenerResponse]$Response, [Parameter(Mandatory)] [int]$StatusCode, [Parameter(Mandatory)] [string]$Text, [string]$ContentType = 'text/plain; charset=utf-8' ) $bytes = [Text.Encoding]::UTF8.GetBytes($Text) $Response.StatusCode = $StatusCode $Response.ContentType = $ContentType $Response.ContentLength64 = $bytes.Length $Response.OutputStream.Write($bytes, 0, $bytes.Length) } function Get-DirectoryListingHtml { param( [Parameter(Mandatory)] [string]$Directory, [Parameter(Mandatory)] [string]$RequestPath ) $encodedTitle = [Net.WebUtility]::HtmlEncode("Index of $RequestPath") $builder = [Text.StringBuilder]::new() [void]$builder.AppendLine('') [void]$builder.AppendLine('') [void]$builder.AppendLine("$encodedTitle") [void]$builder.AppendLine('') [void]$builder.AppendLine('') [void]$builder.AppendLine("

$encodedTitle

    ") if ($RequestPath -ne '/') { [void]$builder.AppendLine('
  • ../
  • ') } foreach ($item in Get-ChildItem -LiteralPath $Directory | Sort-Object @{ Expression = { -not $_.PSIsContainer } }, Name) { $displayName = $item.Name + $(if ($item.PSIsContainer) { '/' } else { '' }) $hrefName = [Uri]::EscapeDataString($item.Name) + $(if ($item.PSIsContainer) { '/' } else { '' }) $encodedName = [Net.WebUtility]::HtmlEncode($displayName) [void]$builder.AppendLine("
  • $encodedName
  • ") } [void]$builder.AppendLine('
') return $builder.ToString() } function Invoke-Serve { param([string[]]$Args) $port = 8000 if ($Args.Count -gt 0) { if (-not [int]::TryParse($Args[0], [ref]$port) -or $port -lt 1 -or $port -gt 65535) { throw "Invalid port: $($Args[0])" } } $root = [IO.Path]::GetFullPath((Get-Location).Path) $rootWithSeparator = $root.TrimEnd('\') + '\' $prefix = "http://127.0.0.1:$port/" $listener = [System.Net.HttpListener]::new() $listener.Prefixes.Add($prefix) try { $listener.Start() Write-Host "Serving $root" Write-Host $prefix Write-Host 'Press Ctrl+C to stop.' while ($listener.IsListening) { $context = $listener.GetContext() $response = $context.Response try { $requestPath = [Uri]::UnescapeDataString($context.Request.Url.AbsolutePath) $relativePath = $requestPath.TrimStart('/').Replace('/', '\') $candidate = [IO.Path]::GetFullPath((Join-Path $root $relativePath)) $insideRoot = $candidate.Equals($root, [StringComparison]::OrdinalIgnoreCase) -or $candidate.StartsWith($rootWithSeparator, [StringComparison]::OrdinalIgnoreCase) if (-not $insideRoot) { Send-TextResponse -Response $response -StatusCode 403 -Text 'Forbidden' continue } if (Test-Path -LiteralPath $candidate -PathType Container) { $indexFile = @('index.html', 'index.htm') | ForEach-Object { Join-Path $candidate $_ } | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 if ($indexFile) { $candidate = $indexFile } else { $html = Get-DirectoryListingHtml -Directory $candidate -RequestPath $requestPath Send-TextResponse -Response $response -StatusCode 200 -Text $html -ContentType 'text/html; charset=utf-8' continue } } if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) { Send-TextResponse -Response $response -StatusCode 404 -Text 'Not found' continue } $fileInfo = Get-Item -LiteralPath $candidate $response.StatusCode = 200 $response.ContentType = Get-MimeType -Path $candidate $response.ContentLength64 = $fileInfo.Length $stream = [IO.File]::OpenRead($candidate) try { $stream.CopyTo($response.OutputStream) } finally { $stream.Dispose() } } catch { if ($response.OutputStream.CanWrite) { Send-TextResponse -Response $response -StatusCode 500 -Text 'Internal server error' } } finally { $response.OutputStream.Close() } } } finally { if ($listener.IsListening) { $listener.Stop() } $listener.Close() } } function Show-Help { @' MS.Open commands open [path] Open a folder, file, or URL reveal Show a file in Explorer copypath [path] Copy a full path to the clipboard which Explain which command will run paths Print PATH entries one per row serve [port] Serve the current folder at 127.0.0.1 ports Show listening TCP ports killport Stop the process listening on a port sha256 Calculate a SHA-256 hash pbcopy [text] Copy arguments or piped input pbpaste Print clipboard text PowerShell functions installed separately: mkcd Create a directory and enter it reload Reload the current PowerShell profile '@ } try { switch ($Command.ToLowerInvariant()) { 'open' { Invoke-Open -Args $Arguments } 'reveal' { Invoke-Reveal -Args $Arguments } 'copypath' { Invoke-CopyPath -Args $Arguments } 'which' { Invoke-Which -Args $Arguments } 'paths' { Invoke-Paths } 'serve' { Invoke-Serve -Args $Arguments } 'ports' { Invoke-Ports } 'killport' { Invoke-KillPort -Args $Arguments } 'sha256' { Invoke-Sha256 -Args $Arguments } 'pbcopy' { Invoke-Pbcopy -Args $Arguments } 'pbpaste' { Invoke-Pbpaste } 'help' { Show-Help } '--help' { Show-Help } '-h' { Show-Help } default { throw "Unknown command: $Command`n`n$(Show-Help)" } } } catch { Write-Error $_.Exception.Message exit 1 }