我正在尝试编写一个shell脚本,在远程服务器上创建一些目录,然后使用scp将文件从我的本地机器复制到远程。以下是我目前所了解到的:

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

每当我运行它,我得到这条消息:

Pseudo-terminal will not be allocated because stdin is not a terminal.

剧本永远挂着。

我的公钥在服务器上是可信的,我可以在脚本之外运行所有命令。什么好主意吗?


当前回答

尝试ssh -t -t(或简称ssh -tt)强制伪tty分配,即使stdin不是终端。

请参见:终止bash脚本执行的SSH会话

从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.

其他回答

还有manual中的选项-T

禁用伪tty分配

The warning message Pseudo-terminal will not be allocated because stdin is not a terminal. is due to the fact that no command is specified for ssh while stdin is redirected from a here document. Due to the lack of a specified command as an argument ssh first expects an interactive login session (which would require the allocation of a pty on the remote host) but then has to realize that its local stdin is no tty/pty. Redirecting ssh's stdin from a here document normally requires a command (such as /bin/sh) to be specified as an argument to ssh - and in such a case no pty will be allocated on the remote host by default.

由于通过ssh执行的命令不需要tty/pty(如vim或top),所以切换到ssh的-t是多余的。 只需使用ssh -T user@server <<EOT…或ssh user@server /bin/bash <<EOT…警告就会消失。

如果<<EOF没有转义或单引号(即<<\EOT或<<'EOT'),这里文档中的变量将在执行ssh ....之前由本地shell展开结果是,这里文档中的变量将保持为空,因为它们仅在远程shell中定义。

因此,如果$REL_DIR既可以被本地shell访问,又可以在远程shell中定义,那么$REL_DIR必须在ssh命令(下面的版本1)之前在here文档之外定义;或者,如果使用<<\EOT或<<'EOT',如果ssh命令对stdout的唯一输出是由转义/单引号here文档中的echo "$REL_DIR"生成的,则ssh命令的输出可以分配给REL_DIR(下面的版本2)。

第三种方法是将here文档存储在一个变量中,然后将该变量作为命令参数传递给ssh -t user@server "$heredoc"(下面是第3版)。

最后但并非最不重要的是,检查远程主机上的目录是否已成功创建(参见:使用ssh检查远程主机上是否存在文件)也不是一个坏主意。

# version 1

unset DEP_ROOT REL_DIR
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"

ssh localhost /bin/bash <<EOF
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
#echo "$REL_DIR"
exit
EOF

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 2

REL_DIR="$(
ssh localhost /bin/bash <<\EOF
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
exit
EOF
)"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 3

heredoc="$(cat <<'EOF'
# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairs
stty -echo -onlcr
DEP_ROOT='/tmp'
datestamp="$(date +%Y%m%d%H%M%S)"
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
stty echo onlcr
exit
EOF
)"

REL_DIR="$(ssh -t localhost "$heredoc")"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"

我添加这个答案是因为它解决了与我遇到的相同错误消息相关的问题。

问题:我在Windows下安装了cygwin,并得到这个错误:伪终端将不会被分配,因为stdin不是终端

解决方案:原来我没有安装openssh客户端程序和实用程序。正因为如此,cygwin使用的是ssh的Windows实现,而不是cygwin版本。解决方案是安装openssh cygwin包。

尝试ssh -t -t(或简称ssh -tt)强制伪tty分配,即使stdin不是终端。

请参见:终止bash脚本执行的SSH会话

从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.

我在Windows下使用emacs 24.5.1通过/ssh连接到一些公司服务器:user@host时遇到了同样的错误。解决我的问题是将“tramp-default-method”变量设置为“plink”,每当我连接到服务器时,我都会提交ssh协议。你需要安装PuTTY的plink.exe才能工作。

解决方案

M-x自定义变量(然后按Enter) tramp-default-method(然后再次按Enter键) 在文本框中输入plink,然后应用并保存缓冲区 每当我尝试访问远程服务器时,我现在使用C-x-f /user@host:,然后输入密码。现在在Windows上的Emacs下正确地建立了到远程服务器的连接。