猿问
下载APP

由于stdin不是终端,因此不会分配伪终端

我正在尝试编写一个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.

脚本永远挂起。


我的公钥在服务器上是可信的,我可以在脚本之外运行所有命令。有任何想法吗?


慕用2447696
浏览 69回答 3
3回答

浮云间

根据zanco的回答,ssh鉴于shell如何解析命令行,你不会提供远程命令。要解决此问题,请更改ssh命令调用的语法,以便远程命令由语法正确的多行字符串组成。可以使用各种语法。例如,由于命令可被输送进bash和sh,大概等贝壳也一样,最简单的解决方法就是结合sshshell调用与here文档:ssh user@server /bin/bash <<'EOT'echo "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT请注意,执行上述操作不会 /bin/bash导致警告Pseudo-terminal will not be allocated because stdin is not a terminal。还要注意,EOT它被单引号括起来,因此bash将heredoc识别为nowdoc ,关闭局部变量插值,以便命令文本按原样传递给。ssh如果您是管道的粉丝,可以按如下方式重写上述内容:cat <<'EOT' | ssh user@server /bin/bashecho "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT同样的警告/bin/bash适用于上述情况。另一种有效的方法是将多行远程命令作为单个字符串传递,使用多层bash变量插值,如下所示:ssh user@server "$( cat <<'EOT'echo "These commands will be run on: $( uname -a )"echo "They are executed by: $( whoami )"EOT)"上述解决方案以下列方式解决了此问题:ssh user@server由bash解析,并被解释为ssh命令,后跟user@server要传递给ssh命令的参数"开始一个内插字符串,在完成后,将包含一个要传递给ssh命令的参数,在这种情况下,该参数将被解释ssh为要执行的远程命令user@server$( 开始执行命令,输出由周围的插值字符串捕获cat是一个输出以下任何文件内容的命令。输出cat将被传递回捕获插值字符串<<开始一个bash heredoc'EOT'指定heredoc的名称是EOT。'围绕EOT 的单引号指定heredoc应该被解析为nowdoc,这是一种特殊形式的heredoc,其中的内容不会被bash插值,而是以字面格式传递在<<'EOT'和之间遇到的任何内容<newline>EOT<newline>都将附加到nowdoc输出EOT终止nowdoc,导致创建一个nowdoc临时文件并传递回调用cat命令。cat输出nowdoc并将输出传递回捕获插值字符串) 总结要执行的命令"结束捕获插值字符串。插值字符串的内容将ssh作为单个命令行参数传递回来,该参数ssh将解释为要执行的远程命令user@server如果您需要避免使用类似的外部工具cat,并且不介意使用两个语句而不是一个语句,请使用read内置的heredoc来生成SSH命令: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}"

慕哥6287543

警告消息Pseudo-terminal will not be allocated because stdin is not a terminal.是由于没有为sshstdin从here文档重定向时指定的命令。由于缺少指定的命令作为参数,ssh首先需要一个交互式登录会话(这需要在远程主机上分配一个pty),但是必须意识到它的本地stdin不是tty / pty。ssh从here文档重定向的stdin通常需要将命令(例如/bin/sh)指定为参数ssh- 在这种情况下,默认情况下不会在远程主机上分配pty。由于没有要执行的命令ssh要求存在tty / pty(例如vim或top),所以-t切换ssh是多余的。只需使用ssh -T user@server <<EOT ...或ssh user@server /bin/bash <<EOT ...,警告就会消失。如果<<EOF未执行转义或单引号(即<<\EOT或<<'EOT'),则本地文档中的变量将在执行之前由本地shell进行扩展ssh ...。结果是here文档中的变量将保持为空,因为它们仅在远程shell中定义。因此,如果$REL_DIR本地shell都可以访问并且在远程shell $REL_DIR中定义,则必须在ssh命令之前在here文档之外定义(下面的版本1); 或者,如果<<\EOT或者<<'EOT'使用,如果stdout命令的唯一输出由转义/单引号文档(下面的版本2)内部生成,ssh则可以分配命令的输出。REL_DIRsshecho "$REL_DIR"第三种选择是将here文档存储在变量中,然后将此变量作为命令参数传递给ssh -t user@server "$heredoc"(下面的版本3)。并且,最后但并非最不重要的是,检查远程主机上的目录是否已成功创建也是不错的(请参阅:使用ssh检查远程主机上是否存在文件)。# version 1unset DEP_ROOT REL_DIRDEP_ROOT='/tmp'datestamp=$(date +%Y%m%d%H%M%S)REL_DIR="${DEP_ROOT}/${datestamp}"ssh localhost /bin/bash <<EOFif [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then&nbsp; &nbsp;echo "creating the root directory" 1>&2&nbsp; &nbsp;mkdir "$DEP_ROOT"fimkdir "$REL_DIR"#echo "$REL_DIR"exitEOFscp -r ./dir1 user@server:"$REL_DIR"scp -r ./dir2 user@server:"$REL_DIR"# version 2REL_DIR="$(ssh localhost /bin/bash <<\EOFDEP_ROOT='/tmp'datestamp=$(date +%Y%m%d%H%M%S)REL_DIR="${DEP_ROOT}/${datestamp}"if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then&nbsp; &nbsp;echo "creating the root directory" 1>&2&nbsp; &nbsp;mkdir "$DEP_ROOT"fimkdir "$REL_DIR"echo "$REL_DIR"exitEOF)"scp -r ./dir1 user@server:"$REL_DIR"scp -r ./dir2 user@server:"$REL_DIR"# version 3heredoc="$(cat <<'EOF'# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairsstty -echo -onlcrDEP_ROOT='/tmp'datestamp="$(date +%Y%m%d%H%M%S)"REL_DIR="${DEP_ROOT}/${datestamp}"if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then&nbsp; &nbsp;echo "creating the root directory" 1>&2&nbsp; &nbsp;mkdir "$DEP_ROOT"fimkdir "$REL_DIR"echo "$REL_DIR"stty echo onlcrexitEOF)"REL_DIR="$(ssh -t localhost "$heredoc")"scp -r ./dir1 user@server:"$REL_DIR"scp -r ./dir2 user@server:"$REL_DIR"

弑天下

即使stdin不是终端,也要尝试ssh -t -t(或ssh -tt简称)强制伪tty分配。从ssh手册页:-T&nbsp; &nbsp; &nbsp; Disable pseudo-tty allocation.-t&nbsp; &nbsp; &nbsp; Force pseudo-tty allocation.&nbsp; This can be used to execute arbitrary&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; screen-based programs on a remote machine, which can be very useful,&nbsp; &nbsp; &nbsp; &nbsp; e.g. when implementing menu services.&nbsp; Multiple -t options force tty&nbsp; &nbsp; &nbsp; &nbsp; allocation, even if ssh has no local tty.
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答