0

我使用的 Bash 命令:

  1. $ ssh user@myserver.com ps -aux|grep -v \"grep\"|grep "/srv/adih/server/app.js"|awk '{print $2}'

    6373
    
  2. $ ssh user@myserver.com echo $(ps -aux|grep -v \"grep\"|grep "/srv/adih/server/app.js"|awk '{print $2}')

    8630
    

第一个结果是正确的,第二个结果会改变我执行它的回显时间。但我不知道为什么它们不同。


我在做什么?

我的工作站资源非常有限,所以我使用远程机器来运行我的 Node.js 应用程序。ssh user@remotebox.com "cd /application && grunt serve"我在调试模式下运行它。当我命令Ctrl+C时,grunt 任务停止,但应用程序仍在调试模式下运行。我只想杀了它,我需要先得到PID。

4

2 回答 2

3

命令替换在运行前由您的本地 shellssh执行。

如果您的本地系统名称是here远程是there,

ssh there uname -n

将打印there

ssh there echo $(uname -n)  # should have proper quoting, too

将在uname -n本地运行,然后发送扩展的命令行echo herethere执行。

另外,除非您特别要求外壳在打印输出之前对输出执行通配符扩展和空白标记化,否则使用echo $(command)无用的。echocommand

此外,grep x | awk { y }无用的使用grep; 它可以而且可能应该被重构为awk '/x/ { y }'——但当然,在这里你正在重新发明pidof,所以更好地使用它。

 ssh user@myserver.com pidof /srv/adih/server/app.js

如果要在本地捕获打印的 PID,则其语法为

pid=$(ssh user@myserver.com pidof /srv/adih/server/app.js)

当然,如果你只需要杀死它,那就是

ssh user@myserver.com pkill /srv/adih/server/app.js
于 2017-12-05T06:09:11.157 回答
2

简短的回答:$(ps ... )命令替换正在本地计算机上运行,​​然后其输出(与echo命令一起)发送到远程计算机。本质上,它正在运行ssh user@myserver.com echo 8630

您的第一个命令也可能没有按照您的预期执行;管道在本地计算机上进行解释,因此它正在运行ssh user@myserver.com ps -aux,将输出管道传输grep 到本地计算机上,将其管道传输到本地计算机上的另一grep台计算机等。我猜你希望整个事情在远程计算机上运行所以结果可以在远程计算机上用于终止进程。

长答案:在 shell 中解析和执行事物的顺序有点混乱;混合使用ssh命令,事情变得更加复杂。基本上,发生的事情是本地 shell 解析命令行,包括将其拆分为单独的命令(由管道分隔;,等),以及扩展$(command)$variable替换(除非它们在单引号中)。然后它删除引号和转义符(他们已经完成了他们的工作)并将结果作为参数传递给各种命令(例如ssh)。ssh获取它的参数,将所有看起来像远程命令的一部分的参数与它们之间的空格粘在一起,然后将它们发送到远程计算机上的外壳,该外壳再次执行此过程

这意味着如果您希望远程 shell 而不是本地 shell 对它们进行解析/操作,那么引用和/或转义诸如$and之类的东西是必要的。|并且引号不会嵌套,因此在整个内容周围加上引号可能不会按您期望的方式工作(例如,如果您不小心,该$2awk 命令可能会在本地计算机上扩展,即使它看起来像在单引号)。

当事情像这样变得混乱时,最简单的方法有时是将远程命令作为此处文档而不是作为ssh命令的参数传递。但是您需要在 here-document 分隔符周围加上引号,以防止$本地 shell 完成各种扩展。像这样的东西:

ssh user@myserver.com <<'EOF'
echo $(ps -aux|grep -v "grep"|grep "/srv/adih/server/app.js"|awk '{print $2}')
EOF

注意:缩进远程命令时要小心,因为文本将按字面意思发送到远程计算机。如果您使用制表符缩进它,您可以将<<-其用作此处的文档分隔符(例如<<-'EOF'),它会删除前导制表符。

编辑:正如@tripleee 指出的那样,不需要多个greps,因为awk它可以自己完成整个工作。也没有必要从结果中排除搜索命令(grep -v grep),因为模式中的“/”字符需要转义,这意味着它不会匹配自己。所以您可以将管道简化为:

ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}'

现在,我一直假设实际目标是kill相关的 pid,并且echo只是为了测试。如果是这样的话,实际的命令最终是:

ssh user@myserver.com <<'EOF'
kill $(ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}')
EOF

如果这不正确,那么echo $( )最好完全跳过整个事情。没有理由捕获管道的输出然后echo它,只需运行它并让它直接输出。

如果pkill(或类似的东西)可用,那么使用它会更简单。

于 2017-12-05T06:20:22.933 回答