由于 stdin 不是终端,因此不会分配伪终端
- 2024-10-09 09:11:00
- admin 原创
- 78
问题描述:
我正在尝试编写一个 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.
而脚本就永远悬而未决。
我的公钥在服务器上是受信任的,我可以顺利运行脚本之外的所有命令。有什么想法吗?
解决方案 1:
尝试ssh -t -t
(或ssh -tt
简称)强制伪tty分配,即使stdin不是终端。
另请参阅:终止由 bash 脚本执行的 SSH 会话
来自 ssh 手册页:
-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.
解决方案 2:
也可以选择-T
手动
禁用伪tty分配
解决方案 3:
根据zanco 的回答ssh
,考虑到 shell 如何解析命令行,您没有向 提供远程命令。要解决此问题,请更改ssh
命令调用的语法,以便远程命令由语法正确的多行字符串组成。
有多种语法可供使用。例如,由于命令可以通过管道传输到bash
和sh
,也可能传输到其他 shell 中,最简单的解决方案就是将ssh
shell 调用与 heredocs 结合起来:
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/bash
echo "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 )"
EOT
ssh user@server "${SSH_COMMAND}"
解决方案 4:
我添加这个答案是因为它解决了我遇到的与相同错误消息相关的问题。
问题:我在 Windows 下安装了 cygwin 并收到此错误:Pseudo-terminal will not be allocated because stdin is not a terminal
解决方案:原来我没有安装 openssh 客户端程序和实用程序。因为 cygwin 使用的是 Windows 版 ssh,而不是 cygwin 版。解决方案是安装 openssh cygwin 包。
解决方案 5:
所有相关信息都在现有答案中,但让我尝试进行务实的总结:
总结:
请通过命令行参数传递要运行的命令:
ssh jdoe@server '...'
+ **`'...'`字符串可以跨越多行**,因此即使不使用此处文档,也可以保持代码的可读性:
`ssh jdoe@server ' ... '`
不要通过stdin传递命令,就像使用here-document时一样:
ssh jdoe@server <<'EOF' # Do NOT do this ... EOF
将命令作为参数传递按原样工作,并且:
伪终端的问题甚至不会出现。
您不需要
exit
在命令末尾添加语句,因为会话将在命令处理完毕后自动退出。
简而言之:通过stdin传递命令是一种与 的设计相悖的机制ssh
,它会导致必须解决的问题。
如果您想了解更多信息,请继续阅读。
可选的背景信息:
ssh
接受在目标服务器上执行的命令的机制是命令行参数:最后的操作数(非选项参数)接受包含一个或多个 shell 命令的字符串。
默认情况下,这些命令在非交互式shell中无人值守运行,不使用(伪)终端(隐含选项),并且
-T
当最后一个命令完成处理时会话自动结束。如果您的命令需要用户交互,例如响应交互式提示,您可以明确请求创建一个pty (pseudo-tty),一个伪终端,以便使用以下选项与远程会话进行交互
-t
;例如:
+ `ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'`
+ 请注意,交互式`read`提示只有在使用 pty 时才能正常工作,因此`-t`需要该选项。
+ 使用 pty 有一个明显的副作用: stdout 和 stderr*合并*并都通过*stdout*报告;换句话说:你失去了常规输出和错误输出之间的区别;例如:
- `ssh jdoe@server 'echo out; echo err >&2' # OK - stdout and stderr separate`
- `ssh -t jdoe@server 'echo out; echo err >&2' # !! stdout + stderr -> stdout`
如果没有这个参数,ssh
就会创建一个交互式的shell - 包括当你通过stdin发送命令时,这就是问题开始的地方:
对于交互式shell,
ssh
通常默认分配一个 pty(伪终端),除非其 stdin 未连接到(真实)终端。
+ **通过 stdin 发送命令意味着 的`ssh`stdin 不再连接到终端,因此*不会*创建 pty,并且会相应地`ssh` *发出警告***:
`Pseudo-terminal will not be allocated because stdin is not a terminal.`
+ **即使是`-t`明确目的是*请求*创建 pty 的选项,*在这种情况下也是**不够*的**:您会收到相同的警告。
- 有点奇怪的是,您必须***双击*选项`-t`才能**强制创建 pty:`ssh -t -t ...`或`ssh -tt ...`表明您*确实、确实是这个意思*。
- 也许要求采取这一非常谨慎的步骤的理由是*事情可能不会按预期进行*。例如,在 macOS 10.12 上,上述命令的明显等效项(通过 stdin 和使用提供命令`-tt`)无法*正常*工作;响应提示后,会话会卡住`read`:
`ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'`
万一您想要作为参数传递的命令使得命令行对于您的系统来说太长(如果它的长度接近getconf ARG_MAX
- 请参阅本文),请考虑先以脚本的形式将代码复制到远程系统(例如使用scp
),然后发送命令来执行该脚本。
在紧急情况下,使用,并通过stdin-T
提供命令,并带有尾随命令,但请注意,如果您还需要交互式功能,则使用代替可能不起作用。exit
`-tt`-T
解决方案 6:
出现警告消息Pseudo-terminal will not be allocated because stdin is not a terminal.
是因为ssh
在从 here document 重定向 stdin 时未指定任何命令。由于缺少指定的命令作为参数,因此ssh
首先需要交互式登录会话(这需要在远程主机上分配 pty),但随后必须意识到其本地 stdin 不是 tty/pty。ssh
从 here document 重定向 的 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 ...
。结果是此处文档中的变量将保持为空,因为它们仅在远程 shell 中定义。
因此,如果既$REL_DIR
可以由本地 shell 访问,又可以在远程 shell 中定义,$REL_DIR
则必须在ssh
命令之前在 here 文档之外定义(下面的版本 1);或者,如果使用<<EOT
或,则如果命令到 stdout 的唯一输出是由转义/单引号的 here 文档内部生成的,则可以将命令的输出分配给。<<'EOT'
`sshREL_DIR
ssh`echo "$REL_DIR"
第三种选择是将此处的文档存储在一个变量中,然后将该变量作为命令参数传递给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"
解决方案 7:
我不知道挂起的原因是什么,但将命令重定向(或管道)到交互式 ssh 通常会导致问题。使用命令作为最后一个参数运行的样式并在 ssh 命令行上传递脚本更为可靠:
ssh user@server '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'
(所有内容都在一个巨大的'
以 -delimited 开头的多行命令行参数中)。
伪终端消息是由于您的-t
命令要求 ssh 尝试使其在远程计算机上运行的环境看起来像是实际的终端,供在那里运行的程序使用。您的 ssh 客户端拒绝这样做,因为它自己的标准输入不是终端,因此它无法将特殊终端 API 从远程计算机传递到本地的实际终端。
你到底想实现什么目标-t
?
解决方案 8:
在阅读了很多这些答案之后,我想分享我最终的解决方案。我添加的所有内容都/bin/bash
在 heredoc 之前,它不再出现错误。
使用这个:
ssh user@machine /bin/bash <<'ENDSSH'
hostname
ENDSSH
而不是这样(出现错误):
ssh user@machine <<'ENDSSH'
hostname
ENDSSH
或者使用这个:
ssh user@machine /bin/bash < run-command.sh
而不是这样(出现错误):
ssh user@machine < run-command.sh
额外的:
如果您仍然想要远程交互式提示,例如,如果您远程运行的脚本提示您输入密码或其他信息,因为以前的解决方案不允许您输入提示。
ssh -t user@machine "$(<run-command.sh)"
如果您还想将整个会话记录在文件中logfile.log
:
ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
解决方案 9:
我在 Windows 上使用 emacs 24.5.1 通过 /ssh:user@host 连接到一些公司服务器时遇到了同样的错误。解决我问题的方法是将“tramp-default-method”变量设置为“plink”,每当我连接到服务器时,我都会省略 ssh 协议。您需要安装 PuTTY 的 plink.exe 才能使其正常工作。
解决方案
Mx customize-variable(然后按 Enter)
tramp-default-method (然后再次按 Enter)
在文本字段上输入 plink,然后应用并保存缓冲区
每当我尝试访问远程服务器时,我现在都会使用 Cxf /user@host:,然后输入密码。现在,在 Windows 上的 Emacs 下可以正确连接到我的远程服务器。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件