For what feels like forever I have had to manually search for and then copy and paste code something like;

lsof -wni tcp:5000 | awk 'NR>1{kill -9 $2}'

So much so that recently I created a gist so at least I had some control of it and had a central place I could search.

I wanted to explain the three main parts to this, partly to help out folk who are lost and partly because I am not too sure myself what all the flags are doing.

First, let me break this down into pseudocode;

  1. Get a list of all processes using port 5000
  2. Tidy up the list we receive to give us just the ID of the processes
  3. Kill the processes

These seem like sensible things to do, so let me talk about each one of them in detail.

Get a list of all processes using a port number

The first part of our one liner is actually the most alien to me. Lets start by finding out what the heck lsof stands for, then we will worry about the flags and the input.

man lsof

Running the man command brings up the manual for a given program, in our case lsof. Immediately we have learned something, it stands of list open files (ls being the command to list files in Unix like operating systems).

At first I thought this seemed very odd, I mean we want to kill stuff on a port, is a port a file? Reading the description gives us (bolding mine);

An open file may be a regular file, a directory, a block special file, a character special file, an executing text reference, a library, a stream or a net-
work file (Internet socket, NFS file or UNIX domain socket.) A specific file or all the files in a file system may be selected by path.

Cool, so that makes a little more sense, but what about the -wni flags we pass into it?

Again man helps us there, reading through the documentation you can find out all about the flags you can use.

  • w — Disables warning messages.
  • n — Inhibits  the  conversion  of network numbers to host names for network files.
  • i — selects the listing of files any of whose Internet address matches the address specified in i. As is in our case, if no address is specified, this option selects the listing of all Internet and x.25 (HP-UX) network files.

Finally, after these flags we pass in our actual input, tcp:5000 - all this is doing is say look for open files on the tcp port 5000.

So what happens when we actually run this command when we have something running on port 5000, I am going to spin up a rails server on that port and run the command;

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 80077 tobyosbourn 11u IPv4 0xadc692f51faed72f 0t0 TCP *:commplex-main (LISTEN)

For our purposes the output gives us the Process ID, which is what we want, but for the sake of completeness lets talk about everything here;

  • COMMAND — The command using port 5000, in our case, ruby
  • PID — The Process ID
  • USER — The User who ran the command
  • FD — The File Descriptor Number for the file. In our case 11u, the 'u' part means there is read and write access on the file.
  • TYPE — The type of node associated with the file, since it is a networking file ours is IPv4
  • DEVICE — This can mean many things, but since we are dealing with a socket this is the base address of the device.
  • SIZE/OFF — This is either the size of the file or the size of the file offset in bytes
  • NODE — In our case this is the internet protocol being used (TCP)
  • NAME — Again this can have many meanings depending on the type of file, for us this is the nice name of port 5000 (something I didn't know!)

Tidy up the output from lsof

As you can see there is a lot of pretty useful output we get when running such a simple command, but for our purposes we only care about the PID, this is where awk comes in.

awk is a program for pattern scanning and processing text, it is crazy powerful and the learning curve isn't as steep as you might imagine.

Lets start by simplifying our command, so instead of;

awk 'NR>1{kill -9 $2}'

We have;

awk 'NR>1{print $2}'

That might still look as clear as mud to you, but let me explain from left to right;

NR>1 says, ignore any line number less than 1, we count from 0 so what this means is, don't worry about the titles in our output.

{} says do the command inside me once per line

print $2 is a bash command, this is saying print whatever is stored in $2. The magic part is that awk has seen that the output has a clear boundary between words so will only print the second word it sees, which in our case is the PID.

Running this print command means the output that was;

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 80077 tobyosbourn 11u IPv4 0xadc692f51faed72f 0t0 TCP *:commplex-main (LISTEN)

Becomes;

80077

Excellent! This is exactly what we want!

Kill the processes

Well, it is *almost* what we want, in our last set of examples we changed our code to print the PID we want. Lets be honest, we don't actually care what the PID is, we just want the process to be killed!

That is when kill -9 comes in.

The flag 9 references the signal that we will send to the process and we pass in the process ID within that process group we want to kill.

The signals you can send are;

  • 1 — HUP (hang up)
  • 2 — INT (interrupt)
  • 3 — QUIT (quit)
  • 6 — ABRT (abort)
  • 9 — KILL (non-catchable, non-ignorable kill)
  • 14 — ALRM (alarm clock)
  • 15 — TERM (software termination signal)

I hope this has helped clear up what is actually happening when you run that command!