terminal will not be allocated because stdin is not a terminal
I am trying to write a shell script that creates some directories on a remote server and then uses scp to copy files from my local machine onto the remote. Here's what I have so far:
ssh -t user@server<<EOT
DEP_ROOT='/home/matthewr/releases'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
echo "creating the root directory"
mkdir $DEP_ROOT
fi
mkdir $REL_DIR
exit
EOT
scp ./dir1 user@server:$REL_DIR
scp ./dir2 user@server:$REL_DIR
Whenever I run it I get this message:
Pseudo-terminal will not be allocated because stdin is not a terminal.
And the script just hangs forever.
My public key is trusted on the server and I can run all the commands outside of the script just fine. Any ideas?
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.
Also with option -T
from manual
Disable pseudo-tty allocation
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/bash
echo "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 the ssh
command, followed by an argument user@server
to be passed to the ssh
command
"
begins an interpolated string, which when completed, will comprise an argument to be passed to the ssh
command, which in this case will be interpreted by ssh
to be the remote command to execute as user@server
$(
begins a command to be executed, with the output being captured by the surrounding interpolated string
cat
is a command to output the contents of whatever file follows. The output of cat
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 format
Any content that is encountered between <<'EOT'
and <newline>EOT<newline>
will be appended to the nowdoc output
EOT
terminates the nowdoc, resulting in a nowdoc temporary file being created and passed back to the calling cat
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 to ssh
as a single command line argument, which ssh
will interpret as the remote command to execute as user@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 )"
EOT
ssh user@server "${SSH_COMMAND}"
链接地址: http://www.djcxy.com/p/17502.html
上一篇: 让ssh在目标机器的后台执行一个命令
下一篇: 终端将不会被分配,因为stdin不是终端