lsoff README

The project's README from github.com/yutat23/lsoff. As one markdown file: README.md.

lsoff

English | 日本語

image

CLI / TUI that lists listening TCP/UDP ports on Windows, Linux, and macOS.

Think of it as a port-focused lsof: quickly see who is holding a port, then kill the process if you need to.

Install

Binaries are attached to GitHub Releases (lsoff-darwin-arm64, lsoff-linux-amd64, lsoff-windows-amd64.exe, …).

Windows (WinGet)

winget install yutat23.lsoff

macOS / Linux (Homebrew)

Install it from the Homebrew tap. Homebrew 6 requires trusting a third-party tap first:

brew tap yutat23/tap
brew trust yutat23/tap
brew install lsoff

Go install

go install github.com/yutat23/lsoff@latest

Build from source

git clone https://github.com/yutat23/lsoff
cd lsoff
go build -o lsoff .

A tagged push (git tag v0.1.0 && git push origin v0.1.0) builds those binaries and publishes the release.

macOS uses libproc, so CGO is required (enabled by a normal go build).

Usage

lsoff                 # TUI (prints a table if stdout is not a TTY)
lsoff 8080            # show processes holding port 8080
lsoff nginx           # search by name, path, PID, cmdline, project, service…
lsoff -q "node 8080"  # space-separated words are AND
lsoff -t              # TCP only (TUI)
lsoff -p              # hide rows with an unknown PID
lsoff -u 53           # UDP port 53
lsoff --json nginx    # search results as JSON
lsoff -k 8080         # kill those processes (with confirmation)
lsoff -k -y 8080      # skip confirmation (required when piped)
lsoff -h
lsoff -v

Example output for a port query:

PROTO  PORT  ADDRESS      PID    PROJECT  PROCESS  PATH                 CMD                   CWD
tcp    8080  127.0.0.1    41233  lsoff    node     /usr/local/bin/node  /usr/local/bin/node   ~/mywork/lsoff

TUI

Key / action What it does
/ / click Search: / ctrl+f Search (port, PID, name, project, path, cmdline; spaces are AND)
↑ / ↓ / j / k / click / wheel Move and select (works while searching too)
ctrl+u / ctrl+d Move one page up / down (works while searching too)
Click a header Sort by that column (click again for descending)
s / S Cycle sort column / toggle ascending-descending
p Hide rows with an unknown PID (toggle)
4 Show IPv4 rows only (press again to clear)
6 Show IPv6 rows only (press again to clear)
y Copy the selected addr:port
a Auto-refresh every 2 seconds (toggle)
enter / space / click ▸ Expand or collapse sockets within a group
h / l Collapse / expand
esc / ctrl+c Clear the search
r Reload the list now
x Kill the selected process (asks first)
q Quit

In the TUI, tcp is green and udp is amber. The selected row uses the highlight background instead. The CLI table stays uncolored so it stays script-friendly. Process names, command lines, and paths are sanitized for the terminal (control characters and ANSI/OSC sequences). JSON keeps the original strings.

Multiple bindings for the same logical process or Docker container endpoint (same protocol and port, such as an IPv4 + IPv6 pair) start collapsed as one row with ▸ and a +N count. enter expands them into a small tree: the head shows ▾, the last child is drawn with └─, and every child before it with ├─. So a pair of sockets shows a single └─ child, and three or more chain as ├─, … , └─. Different ports and protocols remain separate groups; Docker groups use the container identity rather than a PID.

The 4 and 6 filters are mutually exclusive. Press the other key to switch families, or press the same key again to show both.

Known service names (http, postgres, redis, vite, …) are searchable and shown on the SVC footer line. Ambiguous ports such as 3000 are aliases-only and have no single display name. Historic ports (echo, chargen) are not included. JSON may include "service" when a display name exists.

After x, press y to confirm, or n / esc to cancel. Kill verifies that the process is still the same one that was listed: Linux uses pidfd_open (the fd refers to that process, not the PID number), macOS checks pbi_start_tvsec/usec then signals, and Windows checks CreationTime on an open process handle. On Unix it sends SIGTERM first, then SIGKILL if that same process is still alive after 2 seconds. On Windows it uses TerminateProcess. pid 1 and the lsoff process itself are never killed.

CLI -k follows the same rules. If the same PID appears twice (IPv4 and IPv6), it is killed only once. When stdin is not a TTY, -y is required so a pipe cannot kill by accident.

Limitations

How it collects data

No external commands (lsof / ss / netstat).

OS API
Linux /proc/net/{tcp,tcp6,udp,udp6}, /proc/<pid>/{fd,cmdline,cwd}
macOS sysctl net.inet.{tcp,udp}.pcblist64 (all listeners), libproc (owning process and cwd) and sysctl kern.procargs2
Windows IP Helper, QueryFullProcessImageName, NtQueryInformationProcess (cmdline and cwd)

Without permission, PID, path, and cmdline may be empty. Run as root / Administrator in that case.

Docker-published ports are supported as well. lsoff supplements kernel socket discovery with published-port metadata from the local Docker Engine API over /var/run/docker.sock; it does not imply that the container owns a host namespace socket. The current user must have permission to access that socket. If Docker is absent, the socket is unavailable, or access/API calls fail, normal host-process discovery continues unchanged.

Docker rows show a container name such as docker:grafana in PROCESS. The TUI details show the container-side port and protocol, and the container ID. These rows have no PID, so p / -p hides them, and x / -k cannot stop containers.

UDP has no LISTEN state, so sockets bound to a port with no remote peer are shown.

License

MIT

lsoff (yutat23/lsoff)

Searching Database…

255 ·GitHub
这是什么?lsoff 是一个适用于 Windows、Linux 和 macOS 的 CLI/TUI 工具,列出正在监听的 TCP/UDP 端口,让你看到哪个进程占用某个端口,并可选择将其结束。
这是 CLI 工具、TUI 应用,还是两者都是?两者都是:它有可脚本化的 CLI 表格输出,也有带搜索、排序、过滤和结束进程快捷键的交互式 TUI。
怎么安装 lsoff?从 GitHub Releases 下载预编译二进制,在 Windows 上用 WinGet,在 macOS/Linux 上从 Homebrew tap 安装,或运行 go install,也可以从源码构建。
它需要安装 lsof 或 netstat 之类的其他工具吗?不需要,它直接读取内核/操作系统 API(Linux 上是 procfs,macOS 上是 sysctl/libproc,Windows 上是 IP Helper/NtQueryInformationProcess),而不是调用 lsof、ss 或 netstat。
结束进程或读取端口信息有什么坑吗?在 macOS 上,由于没有 pidfd,kill 不是完全原子的,理论上 PID 可能在一个极小的时间窗口内被复用;在 Windows 上,读取 cmdline/cwd 依赖未公开的内部 API,而且 32 位构建无法查看 64 位进程的详细信息。
要看到全部信息需要管理员/root 权限吗?需要,没有提升权限时 PID、路径和命令行信息可能显示为空;要获得完整数据请以 root 或管理员身份运行。
如何安全地结束进程?按下 x 会要求确认,然后核实进程仍是列表中的那一个(Linux 上通过 pidfd,macOS 上通过时间戳检查,Windows 上通过 CreationTime),之后在 Unix 上先发 SIGTERM 再发 SIGKILL,在 Windows 上调用 TerminateProcess。在非 TTY 的管道中,需要 -y 标志来确认结束操作。
它能显示 Docker 容器端口吗?能,lsoff 通过 /var/run/docker.sock 查询本地 Docker Engine API,把容器发布的端口与宿主机进程一起显示,呈现为 docker:grafana 这样没有 PID 的行,因此无法从 lsoff 中结束它们。
在 TUI 里能搜索和过滤端口吗?可以,你可以按端口、PID、名称、项目、路径或 cmdline 搜索,按任意列排序,并用 4 和 6 键切换只显示 IPv4 或只显示 IPv6 的视图。
它会把同一进程的多个套接字分组吗?会,同一进程或容器的绑定(例如 IPv4+IPv6 对)默认折叠成一行并带 +N 计数,按回车可展开成一个小型树状视图。
What are similar projects?bottom, ctop, telegraf, sampler, witr, nvtop, k9s, wtf
Creator
yutat23
Size
0.2 MB
Stars
255
Age
2mo
Commits · 90 Days