Short answer: the $(ps ... )
command substitution is being run on the local computer, and then its output is sent (along with the echo
command) to the remote computer. Essentially, it's running ssh user@myserver.com echo 8630
.
Your first command is also probably not doing what you expect; the pipes are interpreted on the local computer, so it's running ssh user@myserver.com ps -aux
, piping the output to grep
on the local computer, piping that to another grep
on the local computer, etc. I'm guessing that you wanted that whole thing to run on the remote computer so that the result could be used on the remote computer to kill a process.
Long answer: the order things are parsed and executed in shell is a bit confusing; with an ssh
command in the mix, things get even more complicated. Basically, what happens is that the local shell parses the command line, including splitting it into separate commands (separated by pipes, ;
, etc), and expanding $(command)
and $variable
substitutions (unless they're in single-quotes). It then removes the quotes and escapes (they've done their jobs) and passes the results as arguments to the various commands (such as ssh
). ssh
takes its arguments, sticks all the ones that look like parts of the remote command together with spaces between them, and sends them to a shell on the remote computer which does this process over again.
This means that quoting and/or escaping things like $
and |
is necessary if you want them to be parsed/acted on by the remote shell rather than the local shell. And quotes don't nest, so putting quotes around the whole thing may not work the way you expect (e.g. if you're not careful, the $2
in that awk command might get expanded on the local computer, even though it looks like it's in single-quotes).
When things get messy like this, the easiest way is sometimes to pass the remote command as a here-document rather than as arguments to the ssh
command. But you want quotes around the here-document delimiter to keep the various $
expansions from being done by the local shell. Something like this:
ssh user@myserver.com <<'EOF'
echo $(ps -aux|grep -v "grep"|grep "/srv/adih/server/app.js"|awk '{print $2}')
EOF
Note: be careful with indenting the remote command, since the text will be sent literally to the remote computer. If you indent it with tab characters, you can use <<-
as the here-document delimiter (e.g. <<-'EOF'
) and it'll remove the leading tabs.
EDIT: As @tripleee pointed out, there's no need for the multiple grep
s, since awk
can do the whole job itself. It's also unnecessary to exclude the search commands from the results (grep -v grep
) because the "/" characters in the pattern need to be escaped, meaning that it won't match itself.. So you can simplify the pipeline to:
ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}'
Now, I've been assuming that the actual goal is to kill
the relevant pid, and echo
is just there for testing. If that's the case, the actual command winds up being:
ssh user@myserver.com <<'EOF'
kill $(ps -aux | awk '/\/srv\/adih\/server\/app.js/ {print $2}')
EOF
If that's not right, then the whole echo $( )
thing is best skipped entirely. There's no reason to capture the pipeline's output and then echo
it, just run it and let it output directly.
And if pkill
(or something similar) is available, it's much simpler to use that instead.