From Port Number to PID and Working Directory: netstat Combined with pwdx
Tracking down “who is holding this port, and where is the program installed” is a daily ops routine: the alert carries only a port number, and you have to find the process and the deployment directory before you can go read the logs. This post gives a one-shot little script: pass in a port number, get the PID and the process’s working directory directly, with every step of the pipeline explained as it goes.
Verified environment: Rocky Linux 9 container (net-tools 2.0, pwdx from procps-ng). Behavior is identical on CentOS 7/8.
The Core Mechanism in One Sentence
netstat -nlp lists the “PID/program name” behind each listening port, and pwdx translates a PID into the process’s current working directory — two steps in relay, and the port lands on a directory on disk.
The Script
1 |
|
Usage: save as portpath.sh, chmod +x, then run it with the port number as the first argument.
Verified Run
Inside the container, start a test process with python3 on port 8000 (working directory /tmp/webdemo), then run the script:

The PID and path in the output match directly: python3 process 170, working directory /tmp/webdemo. The echo -e at the end uses ANSI color codes to highlight the port number and path, making them stand out in a terminal (the figure shows plain text with color codes stripped).
This pipeline can also be bent to yield other fields: split the 7th column on the slash and take the second segment to get the program name instead of the PID — netstat -nlp | grep :$1 | awk '{print $7}' | awk -F"/" '{print $2}'. When an alert gives you only a port number, reporting “nginx is holding it” is often more useful than reporting a PID first. Change the column number and you can pull the connection state or the listening address — same trick.
What Each Step Does
netstat -nlp | grep :$1: find the listening entry for the given port;awk '{print $7}': take the 7th column, which looks like “PID/program name”;awk -F"/" '{ print $1 }': split on the slash and take the first segment, leaving the bare PID;pwdx $PID: query that process’s current working directory, output looking like “PID: path”;awk -F ' ' '{print $2}': take the second column of pwdx’s output, giving the path without its prefix;- the final
echo -ehighlights the port number and path in color.
The essence of this pipeline is “text as interface”: every column of netstat’s output is the next tool’s input — change the column number and you get the program name (2nd segment of the 7th column after splitting on the slash) or the connection state.
Wrong Way Compared: grep’s Substring Trap
The script’s grep :$1 is a substring match. Querying :80, the listening entries for :8000 and :8080 match too, and the script may hand you the path of a completely unrelated process. Verified (a container listening on both 80 and 8000 at once):
1 | netstat -nlp | grep :80 | awk '{print $4}' |
The output contains both 0.0.0.0:80 and 0.0.0.0:8000, and whichever PID you get depends on which line sorts first — the result is unreliable. The fix is to anchor the match to the end of the line:
1 | netstat -nlp | awk '$4 ~ /:80$/' |
Only the local-address column ending exactly with :80 matches; verified, only the port 80 line remains. The two outputs side by side:

Swap the grep in the script’s first line for this awk and the trap is filled.
Points to Watch
- The substring-matching problem of
grep :$1is covered in the previous section; for production scripts, go straight to the end-anchored awk form. awk -F ' ' '{print $2}'takes only the first path segment of pwdx’s output; if the process’s working directory contains spaces the result is incomplete — when needed, usepwdx $PID | cut -d" " -f2-to take the whole rest.- netstat’s
-pneeds sufficient privileges to show process information; run as root — a regular user sees the listening entries but no PIDs. - On newer systems netstat belongs to the net-tools package (unmaintained), and
ssis the replacement:ss -nlp | awk '$4 ~ /:80$/'is semantically identical and needs no extra install.
Note: ss queries go through the kernel’s netlink diag channel, which the Docker Desktop container kernel used in this test does not support —
ss -nlpreportedRTNETLINK answers: Invalid argument. It works normally on physical machines and cloud hosts; follow ss(8) for usage.
Summary
From a port number in an alert to a deployment directory on disk, there is only one pipeline in between: netstat finds the PID, pwdx finds the directory. Bake that move into a script, and the next port alert won’t send you scrambling to type four or five lines of commands.
This article was rebuilt from the author’s CSDN blog posts written between 2020 and 2024, originally published on CSDN.