Trace Open Files and Listening Sockets with lsof
Port already in use, or a file that will not let go? lsof shows you which process is holding it, and it is rarely the one you suspected. By the end you will be able to identify the process holding a file, see which program owns a listening TCP socket, and turn the result into script-friendly fields. Allow 10 minutes for the first pass; a busy host can take longer, because permissions and container file systems affect what lsof can inspect.
The route
Jump straight to the step you need, or tick off Done means at the end.
The examples use lsof 4.95.0 on Linux. Most queries are ordinary, read-only commands, and nothing here changes a service or a file, so there is nothing to undo.
Before you start
- Open a shell on the Linux machine you want to inspect.
- Confirm lsof is installed and record its revision.
lsof -v | head -4
Checkpoint
On this system the output includes revision: 4.95.0 and the package is lsof 4.95.0-1build3.
Use sudo only when you need to see processes or paths hidden from your own account. Elevated access reveals more data; it does not make a snapshot complete if a file system is unavailable.
1. Inspect one process
Start with a known PID. The shell expands $$ to its own process ID, so there is no placeholder to edit.
lsof -nP -a -p $$ -d 0,1,2
Checkpoint
A header, then rows for file descriptors 0, 1 and 2 where they are open, with columns including COMMAND, PID, USER, FD, TYPE and NAME. The FD values are standard input, output and error; suffixes such as r and w show the access mode.
-pselects a process ID.-dselects file descriptors.-ais the one people forget. Selection options are normally ORed;-amakes them AND together. Without it, asking for a PID and a descriptor set can return far more than the intersection you meant.-nand-Pskip host-name lookups and keep ports numeric, so output is quicker and less surprising.
2. Find who has a path open
Give lsof a path and it lists the processes with that file open. Quote paths containing spaces or shell metacharacters.
sudo lsof -nP -- /var/log/syslog
Checkpoint
Either one or more rows naming the process and descriptor, or no rows if nothing currently has the file open.
The double hyphen ends lsof's options, so a path beginning with a hyphen is treated as a name, not an option. It is a good habit whenever a command mixes options and file names.
For a directory, choose the scope deliberately:
+dsearches that directory and its immediate contents.+Ddescends the entire tree, and can be slow and memory hungry.
sudo lsof -nP +d /var/log
Warning
Do not start with +D / on a production host. It can traverse a huge tree, and lsof may report warnings or incomplete information when it cannot stat a mounted file system. Stop a long-running query with Ctrl-C.
3. List listening TCP sockets
Use the network selector with an explicit protocol state:
sudo lsof -nP -iTCP -sTCP:LISTEN
Checkpoint
Rows with an address such as *:22 or 127.0.0.1:8080 followed by (LISTEN). The process name and PID tell you which service owns the socket. On a machine with no listening TCP sockets, no rows is a valid result.
-iTCPlimits results to Internet TCP files.-sTCP:LISTENselects the TCP state.- Keep
-nPwhile investigating. Without-n, addresses may need reverse DNS; without-P, port numbers may become service names. Both slow the query and make output harder to compare with firewall or service configuration.
4. Combine filters without changing the meaning
To ask for one user's network files, make the intersection explicit. Replace alice with the account you are investigating.
sudo lsof -nP -a -u alice -iTCP
- Values in one set are ORed.
-u alice,bobmeans either user. - Negation comes first. Values beginning with
^exclude matches, and exclusions are applied before other criteria. Avoid mixing negated and non-negated values in a single-uargument unless you have checked the exact selection you get.
To inspect a command family, use its leading command name:
sudo lsof -nP -c nginx
-c matches command names beginning with the characters you give it. It is not a search of the full command line.
If you need PIDs to feed another command, use -t, which prints only process IDs and suppresses warnings:
lsof -t -c sshd
Tip
Treat those IDs as a snapshot. A process can exit or be replaced before your follow-up command uses them.
5. Make output safe for scripts
Aligned columns are fine at a terminal but are not a stable parsing format. -F emits fields, each tagged with a one-character identifier. This selects PID, command, descriptor and name:
sudo lsof -nP -F pcfn -iTCP -sTCP:LISTEN
- One field per line. Each field ends with a newline by default.
- Sets, then files. A process set starts with
p, file sets withf;cis the command andnis the name. - Awkward names? If names can contain newlines or other odd characters, the manpage documents
-F pcfn0, which uses NUL field terminators.
Tip
Parse the field format; never split the aligned columns on whitespace.
Common traps
- Warnings are not "no". Messages such as
can't stat() ...mean lsof could not inspect part of the system. Do not treat that as proof nothing is using a file. Retry the same narrow query withsudo, then investigate the named mount or namespace if the warning stays. Container overlay and namespace file systems commonly make host-wide queries noisy. - Empty can mean invisible. You only see files of processes your account may inspect, and access controls still limit detail. A blank result can mean "not open" or "not visible to this query". Keep the exact command, stderr warnings and time of the snapshot with any incident notes.
- Repeat mode is not proof.
+r 5repeats every five seconds; stop it with Ctrl-C, and do not treat it as evidence of a stable state.
Warning
Avoid -O unless you have a specific reason and have read the manpage's blocking discussion. It bypasses lsof's normal protection around potentially blocking kernel operations, and the query can hang if the kernel does not respond.
Done means
- One process. You can list a process's open descriptors with
-pand-d. - One path. You can find the owner of a file or directory without traversing an enormous tree by accident.
- Listeners. You can find listening TCP sockets with
-iTCPand-sTCP:LISTEN. - AND versus OR. You use
-awhen you mean an intersection, and know ordinary selection sets are ORed. - Script-safe. You keep warnings and use
-Frather than parsing terminal columns.