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不是终端