3

I'm retrieving a list of two PIDs that I want to kill. My pipeline looks something like

ps -ef | grep foo | grep -v grep | awk {'print $2'} | xargs kill -9

Both processes are killed when executing this locally. But when using it with ssh like

ssh foo@<IP Address> "ps -ef | grep foo | grep -v grep | awk {'print $2'} | xargs kill -9"

only one PID out of two is getting deleted. It outputs kill: <PID>: No such process. Could it be trying to kill the second process locally?

5
  • 1
    The pipeline seems a mighty complicated way to say pkill foo. Otherwise, try xargs echo kill to help see what xargs is running. – thrig Mar 8 '16 at 23:31
  • 1
    do ssh foo@<IP Address> "ps -ef | grep foo | grep -v grep | awk {'print $2'} | xargs /bin/echo kill -9" an we will see how your kill argument look like – MAQ Mar 8 '16 at 23:46
  • 2
    try escaping the $2 as \$2 - it's inside double-quotes so will be expanded by your local shell...probably empty so the awk command is actually awk '{print }'. or just use pkill or pgrep as others have recommended, if they are available. – cas Mar 8 '16 at 23:46
  • Try pgrep foo|xargs kill -9 – Wei Mar 8 '16 at 23:47
  • 2
    protip: use grep '[f]oo' and you don't need the | grep -v grep. – Kevin Mar 9 '16 at 0:22
9

The $2 is within double quotes on the local shell command line, so expanded by your local shell (probably to the empty string).

So the command run on the remote host is something like:

 ps -ef | grep foo | grep -v grep | awk {'print '} | xargs kill -9

That awk command is a no-op (prints all the lines) and xargs will eventually run:

 kill -9 <all-the-words-present-in-the-output-of-ps|grep...>

That includes pid and ppid, user names...

Also if the pid of the kill command is in that list, it will kill itself and thus not be able to kill the remaining ones in the list.

Here, you'd want:

ssh foo@<IP Address> 'pkill -KILL -f foo'

(kills all processes whose command line (at least the first few kilobytes of it) contains foo).

Or if the remote system doesn't have a pkill command:

ssh foo@<IP Address> '
  ps -Ao pid= -o args= |
    awk "/[f]oo/ {print \$1}" |
    xargs kill -s KILL'

We use single quotes on the local side (so no variable expansion there), and double quote for the awk code in the remote shell command line, so we need to escape the $ in $1 so that it is not expanded by the remote shell.

awk is a superset of grep, you generally don't need to pipe them together. Best is to tell ps to output only the things you want to match on, here assuming you want to search for foo in the list of arguments displayed by ps as your usage of -f suggests.

If you want to kill all processes of the remote user (here of user foo and sshing as foo), you could do:

 ssh foo@<IP Address> 'kill -s KILL -- -1'

That will kill all the processes that the user has permission to kill. For a normal user, that's all processes whose real or saved set user id is the same as the real or effective user id of the killing process.

Or:

 ssh foo@<IP Address> 'pkill -KILL -U "$(id -u)"'

(or pkill -U foo). To kill all processes whose real user id is the same as the effective user id of the remote user.

Or you could do:

 ssh foo@<IP Address> '
   trap "" HUP
   ps -Ao sid= -o pid= -U "$(id -u)" |
     awk "\$1 != $(ps -eo sid= -p "\$\$") {print \$2}" |
     xargs kill -s KILL'

(checking on sid so that kill doesn't kill itself, the shell, awk or xargs, and ignoring SIGHUP in case killing the user sshd process causes that signal to be sent)

We're assuming the login shell of the remote user is Bourne-like. Those commands would have to be adapted if the login shell of the remote user is of the csh or rc families which have a different syntax for instance.

It's also better practice to refer to signals by their name rather than number as that makes it clearer and the signal number can change from system to system (9 for SIGKILL is quite universal though).

2
  • Do you know any good resources to learn about the fundamentals of order of operations on the command line? I run into confusion in this area often e.g. I would not have expected the print $2 to be expanded before being passed into ssh. – Steve Mar 9 '16 at 23:15
  • 1
    the shell always expands whatever it can (filename globs, variables, aliases, etc) first. So if $2 (or any variable) is either unquoted or is in double-quotes (that aren't, in turn, inside single-quotes and aren't escaped with a backslash) then it will be expanded, even if expansion results in an empty string (the shell doesn't care about the contents, and doesn't care whether the variable even existed beforehand). this is, of course, a simplification and generalisation because there are likely some rare circumstances where it behaves differently but it's true in 99.99% of cases. – cas Mar 11 '16 at 1:06
3

try this in short

 ssh user_name@ip_address "pkill -9 foo"

or if you want long one

 ssh user_name@ip_address "ps aux | grep '[f]oo' | awk '{print \"kill -9 \"\$2}' | bash -v"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.