echo $(command)使用命令输出得到不同的结果

时间:2017-12-05 05:47:05

标签: bash subshell command-substitution

我使用的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

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

我在做什么? 我的工作站资源非常有限,所以我使用远程机器来运行我的节点应用程序。我使用ssh user@remotebox.com运行它" cd / applocation&&咕噜声"在调试模式下。当我命令CTRL+C时,grunt任务停止但应用程序仍在调试模式下运行。我只是想要杀死它,我需要先得到pid。

2 个答案:

答案 0 :(得分:3)

ssh运行之前,命令替换由本地shell执行。

如果您的本地系统名称为here且遥控器为there

ssh there uname -n

将打印there

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

将在本地运行uname -n,然后将展开的命令行echo here发送到there以执行。

另外,echo $(command)useless use of echo,除非您在打印之前明确要求shell在command的输出上执行通配符扩展和空白标记化。

此外,grep x | awk { y }useless use of 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

答案 1 :(得分:2)

简答:在本地计算机上运行$(ps ... )命令替换,然后将其输出(以及echo命令)发送到远程计算机。从本质上讲,它正在运行ssh user@myserver.com echo 8630

你的第一个命令也可能没有达到预期的效果;管道在本地计算机上进行解释,因此它正在运行ssh user@myserver.com ps -aux,将输出汇总到本地计算机上的grep ,将管道连接到另一台{{1}在本地计算机等等我猜测你想要在远程计算机上运行整个事情,以便可以在远程计算机上使用结果来终止进程。

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

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

当事情变得如此混乱时,最简单的方法是将远程命令作为here-document而不是$2命令的参数传递。但是你需要在here-document分隔符周围引用以保持各种ssh扩展不被本地shell完成。像这样:

$

注意:请小心缩进远程命令,因为文本将逐字地发送到远程计算机。如果您使用制表符进行缩进,则可以使用ssh user@myserver.com <<'EOF' echo $(ps -aux|grep -v "grep"|grep "/srv/adih/server/app.js"|awk '{print $2}') EOF 作为此处的文档分隔符(例如<<-),然后删除主要标签。

编辑:正如@tripleee指出的那样,不需要多个<<-'EOF',因为grep可以完成整个工作。从结果(awk)中排除搜索命令也是不必要的,因为&#34; /&#34;模式中的字符需要转义,这意味着它不会匹配自己。所以你可以简化管道:

grep -v grep

现在,我一直在假设实际目标是ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}' 相关的pid,kill就在那里进行测试。如果是这种情况,那么实际的命令就会出现:

echo

如果这不对,那么整个ssh user@myserver.com <<'EOF' kill $(ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}') EOF 事情最好完全跳过。没有理由捕获管道的输出然后echo $( )它,只需运行它并让它直接输出。

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