3

I sometimes have to use kill -9 as a last resort to kill my rails server. Unfortunately, I have to do that often enough that I wrote a helper function in my bash_profile to do it.

If I do it manually -- ps aux | grep '[r]ails], grab the pid (eg. 12345), and kill -9 12345 -- it kills the server and leaves the browser intact. But my function kills the browser too, which I don't want to do:

function running {
  ps -p $(lsof -i :3000 -t) &> /dev/null
}

function k9 {
  if running
  then
    kill -9 $(lsof -i :3000 -t)
  else
    echo "Rails server is not running"
  fi
}

I've confirmed that lsof -i :3000 -t grabs the same pid as I'm getting from ps aux, so what am I doing wrong?

ivan
  • 1,878
  • Unrelated, but are you sure you need kill -9? There are other signals you can try if the default doesn't work. – chepner Aug 12 '14 at 12:10
  • @chepner Certainly wish I didn't, but I tried my other options (-15 etc) and the rails server gets truly stuck – ivan Aug 14 '14 at 02:11

1 Answers1

16

The problem:

With lsof -i :3000 -t, you will look for any processes connected to a port 3000, which may be on the remote side.

In the cases where you use your script, the browser seems to have a connetction to the rails server on port 3000. The difference is in the timing between using the browser and using kill.

That means the browser is listed in the output of lsof too, and killed.

You should better kill processes that look like your rails server, not anything that is using a port 3000, locally or elsewhere.


The general solution:

pkill is the right tool for the job.
For trying what is matched, use pgrep, which matches the same way:

If the command line of the server, as shown in ps, starts with a command name that identifies your server instance, you can use pkill servercommand.
If you need to match a part of the command line arguments, add -f, like pkill -f serverargument.

The processes that are killed by pkill will just be listed by pgrep - so use that to try what will be killed. Add the option -a to pgrep to list the command line, instead of the pid only.


Examples for pgrep / pkill:

As an example for using -f: According to ps, these two instances of xbindkeys are running:

$ ps aux|grep '[x]bindkeys'
26719 ?        S      0:00 xbindkeys -f /tmp/xbindkeysrc-tmp
32046 ?        S      0:00 xbindkeys -f /home/auser/.xbindkeysrc

Now I can find both instances with:

$ pgrep xbindkeys
26719
32046

Or showing the commands:

$ pgrep -a xbindkeys
26719 xbindkeys -f /tmp/xbindkeysrc-tmp
32046 xbindkeys -f /home/auser/.xbindkeysrc

Or match one of the instances by the command line arguments:

$ pgrep -f '/home/auser/.xbindkeysrc'
32046

Without -f, none of the processes are matched:

$ pgrep '/home/auser/.xbindkeysrc'

Finally, sending the process a signal, like with kill:

$ pkill -f '/home/auser/.xbindkeysrc'


Alternative solution based on original approach:

The solution with pkill is much more general, but to follow your original approach, the lsof command can be made more strict to match
"any local server listening on the port",
instead of
"any program using the local port in any way, or using a remote port of that number in any way".

The option -sTCP:LISTEN restricts the list to ports listening on, and, as only local ports can be listened on, also to local ports only.

It could be used as a if test like this:

if lsof -i :3000 -sTCP:LISTEN -t >/dev/null ; then
    echo running
else
    echo not running
fi



Side note on using SIGKILL, as in kill -9:

The KILL signal can be used with pkill just the same way as with kill:

$ pkill -9 -f '/home/auser/.xbindkeysrc'

But in most general cases, that has no advantage above the default signal -15, and in some cases, it breaks things. So it's not a good idea to make it a habit.

Take a look at When should I not kill -9 a process? to understand why you do not want to use -9 by default.

Volker Siegel
  • 17,283
  • I'm not sure what you mean about the timing, but do you know how I can grab the process id more accurately? I looked at the man-page for pkill, but don't see how I can get the pid. In fact, pgrep rails gets nothing, while ps aux | grep rails gets the rails server (and chrome too, as you noted). – ivan Aug 09 '14 at 22:45
  • It depends on the command line of the server what you need to match. Can you show the output of ps aux | grep rails? – Volker Siegel Aug 09 '14 at 22:54
  • As a guess, the option -f could help if rails is not the command name. Try pgrep -f or pgrep -fl. – Volker Siegel Aug 09 '14 at 22:56
  • Indeed, the -f was the key. Thanks! – ivan Aug 09 '14 at 23:04