<# .SYNOPSIS 根据端口号查找并结束占用它的进程,释放端口。 .DESCRIPTION 默认只处理处于 LISTEN(监听)状态、占用该端口的进程。 会先列出进程 PID / 名称 / 命令行,确认后再结束(-Force 可跳过确认)。 结束后自动复查端口是否已释放。优先用 Get-NetTCPConnection, 不可用时回退解析 netstat -ano。 .PARAMETER Port 要释放的端口号(1-65535)。 .PARAMETER Force 跳过交互确认,直接结束进程。 .PARAMETER IncludeClient 连同“已连接到该端口”的客户端进程一起处理(默认只看服务端监听进程)。 .EXAMPLE .\kill-port.ps1 8101 .EXAMPLE .\kill-port.ps1 -Port 8101 -Force #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateRange(1, 65535)] [int]$Port, [switch]$Force, [switch]$IncludeClient ) $ErrorActionPreference = 'Stop' function Get-PortPids { param([int]$Port, [bool]$IncludeClient) $pids = @() try { if ($IncludeClient) { $conns = Get-NetTCPConnection -LocalPort $Port -ErrorAction Stop } else { $conns = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop } $pids = @($conns | Select-Object -ExpandProperty OwningProcess -Unique) } catch { # 回退:解析 netstat -ano $lines = netstat -ano | Select-String -Pattern (":{0}\s" -f $Port) foreach ($l in $lines) { if (-not $IncludeClient -and ($l.Line -notmatch 'LISTENING')) { continue } $parts = ($l.Line -split '\s+') | Where-Object { $_ } $p = $parts[-1] if ($p -match '^\d+$') { $pids += [int]$p } } $pids = @($pids | Select-Object -Unique) } return $pids } Write-Host "查询占用端口 $Port 的进程 ..." -ForegroundColor Cyan $pids = @(Get-PortPids -Port $Port -IncludeClient $IncludeClient.IsPresent) if (-not $pids) { Write-Host "端口 $Port 当前没有被任何进程占用,无需处理。" -ForegroundColor Green return } $myPid = $PID $rows = foreach ($procId in $pids) { $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$procId" -ErrorAction SilentlyContinue [pscustomobject]@{ PID = $procId Name = if ($proc) { $proc.Name } else { '(已退出)' } CmdLine = if ($proc) { $proc.CommandLine } else { '' } } } $rows | Format-Table -AutoSize PID, Name foreach ($r in $rows) { if ($r.CmdLine) { Write-Host " [$($r.PID)] $($r.CmdLine)" -ForegroundColor DarkGray } } # 过滤掉系统空闲进程(0)与脚本自身 $targets = $rows | Where-Object { $_.PID -ne 0 -and $_.PID -ne $myPid } if (-not $targets) { Write-Host "没有可安全结束的进程(均为系统进程或脚本自身)。" -ForegroundColor Yellow return } if (-not $Force) { # 非交互(stdin 被管道/重定向接管)时绝不自动杀进程,必须显式 -Force $interactive = -not ([Console]::IsInputRedirected -or -not [Environment]::UserInteractive) if (-not $interactive) { Write-Host "非交互环境且未指定 -Force,已取消(不会结束任何进程)。要直接释放请加 -Force。" -ForegroundColor Yellow return } $ans = Read-Host "确认结束以上 $($targets.Count) 个进程并释放端口 $Port ? (y/N)" if ($ans -notmatch '^[Yy]') { Write-Host "已取消。" -ForegroundColor Yellow; return } } foreach ($t in $targets) { try { Stop-Process -Id $t.PID -Force -ErrorAction Stop Write-Host "已结束 PID $($t.PID) ($($t.Name))" -ForegroundColor Green } catch { Write-Host "结束 PID $($t.PID) 失败:$($_.Exception.Message)" -ForegroundColor Red } } Start-Sleep -Milliseconds 500 $remain = @(Get-PortPids -Port $Port -IncludeClient $false) if ($remain) { Write-Host "端口 $Port 仍被占用:PID $($remain -join ', ')(可能是系统进程或权限不足,试试以管理员身份运行)" -ForegroundColor Red exit 1 } else { Write-Host "端口 $Port 已释放 OK" -ForegroundColor Green }