将两个 PID 的列表传递给 xargs 只会使用 ssh 杀死第一个

Ste*_*eve 3 shell ssh xargs awk quoting

我正在检索我想要杀死的两个 PID 的列表。我的管道看起来像

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

在本地执行此操作时,两个进程都会被终止。但是当将它与 ssh 一起使用时

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

两个PID中只有一个被删除。它输出kill: <PID>: No such process. 是否试图在本地杀死第二个进程?

Sté*_*las 9

$2是本地shell命令行上双引号内,由当地壳所以扩展(可能为空字符串)。

所以在远程主机上运行的命令是这样的:

 ps -ef | grep foo | grep -v grep | awk {'print '} | xargs kill -9
Run Code Online (Sandbox Code Playgroud)

awk命令是空操作(打印所有行),xargs最终将运行:

 kill -9 <all-the-words-present-in-the-output-of-ps|grep...>
Run Code Online (Sandbox Code Playgroud)

这包括pid和ppid,用户名...

此外,如果 kill 命令的 pid 在该列表中,它将杀死自己,因此无法杀死列表中的其余命令。

在这里,你会想要:

ssh foo@<IP Address> 'pkill -KILL -f foo'
Run Code Online (Sandbox Code Playgroud)

(杀死其命令行(至少它的前几千字节)包含的所有进程foo)。

或者如果远程系统没有pkill命令:

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

我们在本地端使用单引号(所以那里没有变量扩展),并在远程 shell 命令行中对 awk 代码使用双引号,所以我们需要对$in进行转义,$1以便它不会被远程 shell 扩展。

awk是 的超集grep,您通常不需要将它们通过管道连接在一起。最好是告诉ps只输出你想要匹配的东西,这里假设你想foo在 ps 显示的参数列表中搜索你的使用-f建议。

如果你想杀死远程用户的所有进程(这里是用户 foo 和 sshing 为 foo),你可以这样做:

 ssh foo@<IP Address> 'kill -s KILL -- -1'
Run Code Online (Sandbox Code Playgroud)

这将杀死用户有权杀死的所有进程。对于普通用户来说,就是所有进程的真实或保存的设置用户id与被杀进程的真实或有效用户id相同的进程。

或者:

 ssh foo@<IP Address> 'pkill -KILL -U "$(id -u)"'
Run Code Online (Sandbox Code Playgroud)

(或pkill -U foo)。杀死所有真实用户 id 与远程用户的有效用户 id 相同的进程。

或者你可以这样做:

 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'
Run Code Online (Sandbox Code Playgroud)

(检查sid以便kill不会杀死自身、外壳awkxargs,并忽略 SIGHUP 以防杀死用户sshd进程导致发送该信号)

我们假设远程用户的登录 shell 类似于 Bourne。如果远程用户的登录 shell 是具有不同语法的 csh 或 rc 系列,则必须调整这些命令。

更好的做法是通过名称而不是编号来引用信号,因为这样会更清晰,并且信号编号可以随系统变化(尽管 SIGKILL 为 9 是非常普遍的)。