In general ssh
is able to build a command from many arguments it gets. This kinda crates an impression the arguments are passed to the remote side as an array. They are not; the remote command is always a single string to be interpreted by the remote shell.
Your command:
# locally
ssh -t host1 ssh host2 "cat < /tmp/test.txt"
is equivalent to this:
# locally
ssh -t host1 'ssh host2 cat < /tmp/test.txt'
They both result in the same command being passed to the shell on host1. The command is:
# on host1
ssh host2 cat < /tmp/test.txt
The above command makes the shell on host1 try to redirect stdin of this ssh
. No such file or directory
comes from this redirection attempt on host1. If the redirection worked, the string passed to a shell on host2 would be cat
.
Your desired command on host1 is:
# on host1
ssh host2 "cat < /tmp/test.txt"
which does not involve any redirection on host1 and passes cat < /tmp/test.txt
to a shell on host2.
There are many forms of local command that result in passing exactly cat < /tmp/test.txt
to a shell on host2. This is the one I would use:
# locally
ssh -t host1 'ssh host2 "cat < /tmp/test.txt"'
The outer quotes (single-quotes in this case) are for the local shell. The inner quotes (double-quotes in this case) are for the shell on host1. The command run on host1 is exactly your desired command.
More insight here: How to execute complex command line over ssh?
Note ssh -t host1 …
allocates a tty on host1, which is useful if ssh host2 …
on host1 asks for password (I suspect this is the reason you used ssh -t
), but it may distort the content of /tmp/test.txt
. In other words, if you locally redirected output to a regular file, then the file might not be identical to the original /tmp/test.txt
. You cannot reliably transfer arbitrary data this way (see ssh
with separate stdin, stdout, stderr AND tty).