通过SSH执行kill命令报错排查:无kill正常带kill异常
SSH远程杀死进程时的命令解析问题与解决方法
这个问题我太熟了!你遇到的报错根源是本地shell提前解析了命令里的管道和命令替换符$(),导致远程主机收到的命令完全乱套了。
为什么不带kill时命令正常?
当你执行ssh someUser@someHost ps -aux | grep 'screen -S cats' | awk '{print $2}'时,整个ps -aux | grep 'screen -S cats' | awk '{print $2}'是作为完整的命令字符串传给远程shell的,远程主机自己会去解析执行这些管道和命令,所以一切正常。
带kill时为啥报错?
但加上kill $(...)后,本地shell会先把$(ps -aux | grep 'screen -S cats' | awk '{print $2}')这部分在本地执行,把得到的结果作为参数传给ssh。这就导致两个问题:
- 本地执行
grep 'screen -S cats'时,-S会被当成grep的命令选项(grep确实有-S参数),但这个选项需要后续参数,所以直接报错grep: invalid option -- 'S'。 - 就算本地有匹配的进程,远程主机收到的命令会变成
kill [本地进程ID],而远程主机上根本没有这些进程ID,自然也会出错。
解决方法
下面给你几个靠谱的解决方案,按简洁度排序:
方案1:用pgrep简化命令(最推荐)
pgrep -f可以直接匹配整个命令行,省去管道和awk的麻烦,而且用单引号包裹整个远程命令,让本地shell完全不解析:
ssh someUser@someHost 'kill $(pgrep -f "screen -S cats")'
方案2:用单引号包裹完整远程命令
把整个需要远程执行的命令用单引号括起来,强制本地shell把它当成一个完整字符串传给远程,同时注意转义远程shell需要的特殊字符:
ssh someUser@someHost 'kill $(ps -aux | grep "screen -S cats" | grep -v grep | awk "{print \$2}")'
- 加上
grep -v grep是为了避免匹配到grep进程本身; - 把awk里的
$2转义成\$2,防止远程shell把它当成位置参数解析。
如果你习惯全用单引号嵌套,也可以这么写(可读性稍差,但无需转义$):
ssh someUser@someHost 'kill $(ps -aux | grep '\''screen -S cats'\'' | grep -v grep | awk '\''{print $2}'\'')'
这里的'\''是shell里单引号嵌套的技巧:先闭合当前单引号,用转义的单引号表示实际的单引号,再重新打开单引号。
方案3:用ssh的-t参数(特殊场景用)
如果远程shell有特殊配置,偶尔需要强制分配伪终端,可以加上-t参数,不过一般前面的方法就够了:
ssh -t someUser@someHost 'kill $(pgrep -f "screen -S cats")'
内容的提问来源于stack exchange,提问作者Alina
相关产品推荐
相关产品推荐

