Pseudo-terminal will not be allocated because stdin is not a terminal
Try ssh -t -t
(or ssh -tt
for short) to force pseudo-tty allocation even if stdin isn't a terminal.
See also: Terminating SSH session executed by bash script
From ssh manpage:
-T Disable pseudo-tty allocation.-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.
Per zanco's answer, you're not providing a remote command to ssh
, given how the shell parses the command line. To solve this problem, change the syntax of your ssh
command invocation so that the remote command is comprised of a syntactically correct, multi-line string.
There are a variety of syntaxes that can be used. For example, since commands can be piped into bash
and sh
, and probably other shells too, the simplest solution is to just combine ssh
shell invocation with heredocs:
ssh user@server /bin/bash <<'EOT'echo "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT
Note that executing the above without /bin/bash
will result in the warning Pseudo-terminal will not be allocated because stdin is not a terminal
. Also note that EOT
is surrounded by single-quotes, so that bash
recognizes the heredoc as a nowdoc, turning off local variable interpolation so that the command text will be passed as-is to ssh
.
If you are a fan of pipes, you can rewrite the above as follows:
cat <<'EOT' | ssh user@server /bin/bashecho "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT
The same caveat about /bin/bash
applies to the above.
Another valid approach is to pass the multi-line remote command as a single string, using multiple layers of bash
variable interpolation as follows:
ssh user@server "$( cat <<'EOT'echo "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT)"
The solution above fixes this problem in the following manner:
ssh user@server
is parsed by bash, and is interpreted to be thessh
command, followed by an argumentuser@server
to be passed to thessh
command"
begins an interpolated string, which when completed, will comprise an argument to be passed to thessh
command, which in this case will be interpreted byssh
to be the remote command to execute asuser@server
$(
begins a command to be executed, with the output being captured by the surrounding interpolated stringcat
is a command to output the contents of whatever file follows. The output ofcat
will be passed back into the capturing interpolated string<<
begins a bash heredoc'EOT'
specifies that the name of the heredoc is EOT. The single quotes'
surrounding EOT specifies that the heredoc should be parsed as a nowdoc, which is a special form of heredoc in which the contents do not get interpolated by bash, but rather passed on in literal formatAny content that is encountered between
<<'EOT'
and<newline>EOT<newline>
will be appended to the nowdoc outputEOT
terminates the nowdoc, resulting in a nowdoc temporary file being created and passed back to the callingcat
command.cat
outputs the nowdoc and passes the output back to the capturing interpolated string)
concludes the command to be executed"
concludes the capturing interpolated string. The contents of the interpolated string will be passed back tossh
as a single command line argument, whichssh
will interpret as the remote command to execute asuser@server
If you need to avoid using external tools like cat
, and don't mind having two statements instead of one, use the read
built-in with a heredoc to generate the SSH command:
IFS='' read -r -d '' SSH_COMMAND <<'EOT'echo "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOTssh user@server "${SSH_COMMAND}"