22

I'm using CentOS 7. I want to get the PID (if one exists) of the process running on port 3000. I would like to get this PID for the purposes of saving it to a variable in a shell script. So far I have

[rails@server proddir]$ sudo ss -lptn 'sport = :3000'
State      Recv-Q Send-Q                           Local Address:Port                                          Peer Address:Port
Cannot open netlink socket: Protocol not supported
LISTEN     0      0                                            *:3000                                                     *:*                   users:(("ruby",pid=4861,fd=7),("ruby",pid=4857,fd=7),("ruby",pid=4855,fd=7),("ruby",pid=4851,fd=7),("ruby",pid=4843,fd=7))

but I can't figure out how to isolate the PID all by itself without all this extra information.

4
  • sudo ss -lptnH "sport = :22" | awk -F " " '{printf $6}' | sed 's/.\+pid=\([0-9]\+\).\+/\1/g'. Do you need explanation?
    – user996142
    May 14, 2018 at 20:17
  • 2
    No explanation needed but this results in a "ss: invalid option -- 'H'" error.
    – Dave
    May 14, 2018 at 21:51
  • You probably have an old ss without of this option. Should work even without of it: sudo ss -lptn "sport = :22" | awk -F " " '{printf $6}' | sed 's/.\+pid=\([0-9]\+\).\+/\1/g'
    – user996142
    May 15, 2018 at 1:20
  • 2
    @user996142 not a very helpful comment. might be a good answer though May 15, 2018 at 5:14

5 Answers 5

36

Another possible solution:

lsof -t -i :<port> -s <PROTO>:LISTEN

For example:

# lsof -i :22 -s TCP:LISTEN
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1392 root    3u  IPv4  19944      0t0  TCP *:ssh (LISTEN)
sshd    1392 root    4u  IPv6  19946      0t0  TCP *:ssh (LISTEN)
# lsof -t -i :22 -s TCP:LISTEN
1392
4
  • Tried "lsof -t -i :3000 TCP:LISTEN" and got the error, "lsof: status error on TCP:LISTEN: No such file or directory"
    – Dave
    May 14, 2018 at 21:50
  • @Dave Run it without TCP:LISTEN. May 14, 2018 at 23:00
  • 2
    @Dave I've miss -s key. I've fix it in pattern. An example had this key. May 14, 2018 at 23:03
  • Right tool for the job. May 15, 2018 at 16:03
10

Try this:

pid=$(fuser 3000/tcp 2>/dev/null)

(requires psmisc package)

Please note, that this is reliable only when run by the user root. Others user can only hope to find processes running with the same user.


Boring explanation for root only access with an example here.
Whatever the method used (fuser, ss, lsof, ...) they all end up matching the available list of process' descriptors to an available list of network connections (eg for tcp it's available in /proc/net/tcp).
For example, trying to get the pid using port 22/tcp (with 22 = 0x0016) would end up doing this kind of comparison:

Entry from /proc/net/tcp:
0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 141408 1 000000000a9ac1b5 100 0 0 10 0

with:
dr-x------. 2 root root 0 May 14 17:59 /proc/358/fd lrwx------. 1 root root 64 May 14 17:59 /proc/358/fd/3 -> socket:[141408]

As this fd descriptor is only available for its user (which happen to be root in this example) or root, only that user or root can find out the pid is 358.

0
5

While lsof's -t is the simplest way to get the PID, lsof also has ways to select other fields using the -F option:

$ lsof -F'?'
lsof:   ID    field description
     a    access: r = read; w = write; u = read/write
     c    command name
     d    device character code
     D    major/minor device number as 0x<hex>
     f    file descriptor (always selected)
     G    file flaGs
     i    inode number
     k    link count
     K    task ID (TID)
     l    lock: r/R = read; w/W = write; u = read/write
     L    login name
     m    marker between repeated output
     n    comment, name, Internet addresses
     o    file offset as 0t<dec> or 0x<hex>
     p    process ID (PID)
     g    process group ID (PGID)
     P    protocol name
     r    raw device number as 0x<hex>
     R    paRent PID
     s    file size
     S    stream module and device names
     t    file type
     T    TCP/TPI info
     u    user ID (UID)
     0    (zero) use NUL field terminator instead of NL

With output like so (note that PID and file descriptors are always printed):

$ sudo lsof -F cg -i :22 -s TCP:LISTEN 
p901
g901
csshd
f3
f4

So if you wanted the process group ID instead of the PID, you could do:

$ sudo lsof -F g -i :22 -s TCP:LISTEN | awk '/^g/{print substr($0, 2)}'
901
2

This is what you need exactly

sudo lsof -n -i :3000  | awk '/LISTEN/{print $2}'
12726
12730
12732
1

Caveat: I can only test this on RedHat.

Should be possible with netstat?

 sudo netstat -npl --inet | awk '/:3000/' | awk -F "[ /]+" '{print $7}'

-n for numeric ports
-l for listening ports
-p to see PIDs

You can use --inet or --inet6 switches to tell netstat to only look for IPv4 or IPv6 respectively, otherwise you might get two results.

Alternatively you can tell awk to print only once

sudo netstat -npl | awk '/:3000/' | awk -F "[ /]+" '{print $7; exit}' 

In awk we just use the '/' from netstat's output of PID/program as a separator.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .