Passing a list of two PIDs to xargs only kills the first using ssh

11,639

Solution 1

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).

Solution 2

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"
Share:
11,639

Related videos on Youtube

Steve
Author by

Steve

Updated on September 18, 2022

Comments

  • Steve
    Steve over 1 year

    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?

  • Steve
    Steve about 8 years
    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.
  • Alessio
    Alessio about 8 years
    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.