0

I want to execute the following shell command on a remote machine:

/bin/retail -f -u 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out

This command uses a binary called retail, which basically is a normal Linux tail with some extra sugar. For more information you can check out this github repo.

When I input the command above inside a shell on the remote machine itself, it does what I want, so the command as it is written is correct from the functional perspective. It does what I want.

Problem is I want to execute that command within a script on my laptop and trigger the remote machine.

This is what I tried, but all commands do not print the desired output, instead they print no log line at all:

ssh sshuser@my-server.net.bar /bin/retail -f -u "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out

Then I tried escaping [ and ] or using ' instead of " and probably every combination of it, but everything didn't work.

The desired output is: Behave like a normal tail and then exit if the new line matches the specified one in the string. Again, when I execute it on the remote machine it works as described.

I was asking myself: Maybe retail can't function over ssh? I have tried the normal Unix tail and it works over ssh. The Author of retail says its POSIX compliant and one can replace his system tail with it. So, I guess it is also not a problem of the binary itself.

So the problem really is: How can I format the argument of the ssh command, so that it picks up the command correctly?


Response to comment of Jaken551:

If you mean:

ssh sshuser@my-server.net.bar '/bin/retail -f -u "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out'

Then, yes I have tried that already. It also prints no log line and does not return to the input prompt.


Response to answer of Gerard H. Pille:

Unfortunately the same behaviour. Does not print log and does not return to input prompt. For completeness, I tried this:

ssh sshuser@my-server.net.bar /bin/retail -f -u \'INFORMATION \\[main\\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \\[\\\"ajp-nio-8009\\\"\\]\' /opt/tomcat/logs/catalina.out

Response to answer of Patrick:

Unfortunately, also a negative. For completeness, I tried both of the suggested solutions. Here the first one:

CMD="$(printf "%q " /bin/retail -f -u 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out)"
ssh sshuser@my-server.net.bar "$CMD"

Result: Does not print log and does not return to input prompt.

And the second one:

ssh -T sshuser@my-server.net.bar <<'EOF'
/bin/retail -f -u 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out
EOF

Result: Outputs a Pseudo-Terminal, but not the log and does not return to the input prompt:

 Pseudo-terminal will not be allocated because stdin is not a terminal.
Linux my-remote-server 4.1.15-1-lts #1 SMP Tue Dec 15 20:54:13 CET 2015 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Since I tried out a couple of solutions now, I should try out invoking a sh-script, which contains the retail command as Gerard H. Pille suggested. I still would like to have nice one-liner when possible (without writing a script that contains the line).


Response to Archemar answer:

I took the original command, which I specified and added a -t to it, resulting in the following:

ssh -t sshuser@my-server.net.bar /bin/retail -f -u "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out

and it worked. Archemar you should write your comment as an answer so i can checkmark it.

  • Have you wrapped the remote command /bin/retail -f -u "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out in single quotes? Such as sshuser@my-server.net.bar 'command args' . – Jaken551 Mar 16 '18 at 9:06
  • I suspect something you're telling us is incorrect. There is no reason why some of these would work locally and not remote unless details are being left out. In one of your responses, the output even indicates that the command you ran is not the command you say you ran (the one about Pseudo-terminal will not be allocated ...). – Patrick Mar 16 '18 at 12:19
  • @Patrick: I can understand you are being pissed, but I did not exclude any crucial information. – kiltek Mar 16 '18 at 12:21
  • What makes you think I'm pissed? I'm not. I'm simply stating that we cannot help you without accurate, complete, information. – Patrick Mar 16 '18 at 12:22
  • It is what it is, I provided every bit of information. – kiltek Mar 16 '18 at 12:22
3

sometime commands deal with terminals, since this is not a $PATH issue, you might whish for ssh to allocate a tty.

as per man ssh

-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very use‐ ful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.

simply use

ssh -t sshuser@host ...
2

Jaken551's recommendation is close to the actual solution, but is slightly off due to the extra level of nested quotation. Lets take a look at what's going on with this first.

My usual technique for this is to just throw an echo on the front of the command (it's not perfect, but it's usually good enough).

# Your original command which you say works locally
$ echo 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out
INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\] /opt/tomcat/logs/catalina.out

# The command though ssh
$ ssh example.com echo "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out
INFORMATION [main] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler [ajp-nio-8009] /opt/tomcat/logs/catalina.out

# The command through ssh with the quotes
$ ssh example.com 'echo "INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]" /opt/tomcat/logs/catalina.out'
INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \["ajp-nio-8009"\] /opt/tomcat/logs/catalina.out

So notice the difference in the outputs. In both of the ssh versions, the escaping is different.

What's going on in the first ssh example is that the first level of quotation is getting stripped by the local shell, then ssh is receiving multiple arguments (echo, INFORMATION \[main\] ...), which it then concatenates together into a single argument echo INFORMATION \[main\] ..., which then gets evaluated through the shell on the remote side as sh -c 'echo INFORMATION \[main\] ...'.

In the second ssh example, again the first level of quoting gets stripped by the local shell so ssh receives echo "INFORMATION \[main\] ...", which then gets evaluated on the remote side as sh -c 'echo "INFORMATION \[main\] ..."'. So it's a little better, but you're still dealing with 2 levels of shells stripping your quotations & escapes.

 

There are 2 ways you can approach this. The first is to instead pass your command on STDIN, which will ensure your command is only getting processed by a shell once, at the actual execution time:

ssh -T example.com <<'EOF'
echo 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out
EOF
INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\] /opt/tomcat/logs/catalina.out

However this means that the remote command does not have access to your TTY or STDIN, so if the command needs STDIN, it won't work.

The second approach is to have the shell add an extra level of quotation for you.

$ CMD="$(printf "%q " echo 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out)"
$ ssh example.com "$CMD"
INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\] /opt/tomcat/logs/catalina.out

(you don't need the intermediary $CMD, and you could do an inline ssh ... "$(printf "%q " ...)", I just did it separately for legibility)

 

TL;DR: Either of the following will work for you:

ssh -T sshuser@my-server.net.bar <<'EOF'
/bin/retail -f -u 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out
EOF

CMD="$(printf "%q " /bin/retail -f -u 'INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]' /opt/tomcat/logs/catalina.out)"
ssh sshuser@my-server.net.bar "$CMD"
1

Your job would have been much easier if you'd just created a script on the other system that did what you wanted, and then called that script without any parameters. An alternative is creating a script on the other side, that shows you what parameters it receives, something like echo_args.sh:

#!/bin/ksh

echo "$0 $@"
ARG=1
while [ $# -gt 0 ]
do
  echo "$ARG: $1"
  shift
  (( ARG++ ))
done

Then you can call:

ssh tomcat@rmgdmzwebapp02.dmz.local bin/echo_args.sh  -f -u \'INFORMATION \\[main\\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \\[\\\"ajp-nio-8009\\\"\\]\' /opt/tomcat/logs/catalina.out

and you get:

bin/echo_args.sh -f -u INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\] /opt/tomcat/logs/catalina.out
1: -f
2: -u
3: INFORMATION \[main\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \[\"ajp-nio-8009\"\]
4: /opt/tomcat/logs/catalina.out

which shows you that the parameters arrive correctly on the other side. Now you just replace echo_args.sh by your retail programme:

ssh sshuser@my-server.net.bar /bin/retail -f -u \'INFORMATION \\[main\\] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler \\[\\\"ajp-nio-8009\\\"\\]\' /opt/tomcat/logs/catalina.out

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.