有时我会做类似

ssh user@host sudo thing


的事情,并提醒我ssh默认情况下不分配伪tty。为什么不呢?如果将ssh别名为ssh -t,我将失去什么好处?

评论

>提醒我,ssh不会分配伪tty会发生什么?了解问题所在将丰富问题。

@Air我没有尝试解决的问题。我试图理解如何实现ssh。这个问题很明确,Andrew B的回答很好地解决了这个问题。可以这样总结答案:运行ssh -t总是很糟糕,因为它可能导致某些命令以奇怪的方式中断。而运行需要一个PTY却没有一个PTY的命令会产生一条明确的错误消息,提示您需要一个终端。

#1 楼

主要区别在于交互性的概念。这类似于在脚本内部本地运行命令,而不是自己键入命令。区别在于,远程命令必须选择默认值,并且非交互式最安全。 (通常是最诚实的)

STDIN


如果分配了PTY,应用程序可以检测到这一点并知道可以安全地提示用户进行其他输入破事。如果没有终端,有很多程序将跳过提示用户输入的步骤,这是一件好事。否则将导致脚本不必要地挂起。
在命令执行期间,您的输入将发送到远程服务器。这包括控制序列。尽管Ctrl-c中断通常会导致ssh命令上的循环立即中断,但是您的控制序列将被发送到远程服务器。这导致需要“敲击”击键以确保它在控制离开ssh命令时,但在下一个ssh命令开始之前到达。

我警告不要在无人参与的脚本中使用ssh -t,例如作为老兄。非交互式外壳程序要求远程命令交互地进行输入操作,这会引起各种麻烦。

您还可以在自己的外壳程序脚本中测试终端的存在。要使用较新版本的bash测试STDIN:

# fd 0 is STDIN
[ -t 0 ]; echo $?


STDOUT



ssh别名为ssh -t时,您可以期望得到您的行尾有多余的回车符。它可能对您不可见,但在那里。通过管道连接到^M时,它将显示为cat -e。然后,您必须付出额外的努力,以确保不会将此控制代码分配给变量,尤其是如果要将输出插入数据库中时。
程序也有可能会认为它们可以呈现对文件重定向不友好的输出。通常,如果您要将STDOUT重定向到文件,程序将识别出您的STDOUT不是终端,并省略任何颜色代码。如果STDOUT重定向来自ssh客户端的输出,并且有一个与客户端的远程端相关联的PTY,则远程程序无法进行这种区分,并且最终将在输出文件中出现终端垃圾。将输出重定向到连接的远程端上的文件仍应可以按预期的方式运行。

这里与之前的bash测试相同,但对于STDOUT:

# fd 1 is STDOUT
[ -t 1 ]; echo $?



虽然可以解决这些问题,但您不可避免地会忘记围绕它们设计脚本。我们所有人都在某个时候做。您的团队成员可能也没有意识到/记住此别名,这将在您编写使用您别名的脚本时给您带来麻烦。

ssh别名为ssh -t您将违反最不令人惊讶的设计原则的地方;人们将遇到他们未曾预料到的问题,并且可能不了解造成这些问题的原因。

评论


几乎给人一种印象,就是我曾经在一支做到这一点的团队工作过……

–安德鲁(Andrew B)
2014年5月6日17:14

在此答案涵盖的范围内,这很棒。但是问题的范围更广,本质上是想了解所有差异(期望值)。附加信息:在进程级别,-t首先分配一个tty,然后运行外壳程序(沿途采购/ etc / profile和〜/ .bash_profile),然后运行命令。如果没有-t,ssh将INSTEAD源不同的env文件(/etc/bash.bashrc,然后是〜/ .bashrc),然后运行您的命令。这意味着您会看到每种行为都有很大不同:$ PATH较短,可能是bash'一元'错误,因为您假设的ENV变量不存在...

–斯科特·普里夫(Scott Prive)
'18 / 12/1在18:08

@Crossfit在另一个答案的评论中,我们讨论了登录外壳与非主题,但我们并未详尽分析环境差异,因为OP已经考虑了针对其特殊需求的问题。请随时在您认为有余地的地方添加详细信息,因为这可能会对其他参与此问答的人有所帮助。

–安德鲁(Andrew B)
18/12/1在22:02



#2 楼

SSH转义/控制字符和二进制文件的传输
其他答案中未提及的一个优势是,在不使用伪终端的情况下运行时,不支持SSH转义字符,例如~C;这使程序可以安全地传输可能包含这些和其他控制字符的二进制文件。
没有伪终端,字符就被“按原样”传输到远程主机:
$ printf "one\ntwo\n~Cthree\n" | ssh user@host tee 1
one
two
~Cthree

如果尝试强制分配伪终端,则包含~C会导致打印ssh>提示符,以允许用户输入SSH命令,从而中断传输。
$ printf "one\ntwo\n~Cthree\n" | ssh -tt user@host tee 2

ssh>
one
two
three
one
two
three

A ~.顺序更糟,因为它导致没有数据传输:
$ printf "one\ntwo\n~.three\n" | ssh -tt user@host tee 2
Connection to host closed.

软件流控制
分配伪终端时,软件流控制字符(XON / XOFF)也可能会被特殊对待。
$ printf "one\ntwo\n^Sthree\n" | ssh user@host tee 1
one
two
three

$ ssh user@host cat -vet 1
one$
two$
^Sthree$

使用伪终端时,Ctrl-S字符被解释为暂停输入流的信号,因此遇到此字符后将无法再发送任何数据(除非稍后后面是Ctrl-Q字符)输入流)。
在这种情况下,强制分配一个伪终端导致对端的文件为空。
$ printf "one\ntwo\n^Sthree\n" | ssh -tt user@host tee 2
one
two

在这种情况下,强制对伪终端的分配导致对包含所有三行但不包含三行的远程文件控制字符:
$ printf "one\ntwo\n^Sthree^Q\n" | ssh -tt user@host tee 2
one
two
three
one
two
three

$ ssh user@host cat -vet 2
one$
two$
three$

行尾字符的转换
通过伪终端,换行字符(十进制,ASCII的十六进制0A)也被转换为CRLF序列(十六进制的0D0A) ASCII)。
从上一个示例开始:
$ ssh -t user@host cat 1 | cat -vet
one^M$
two^M$
^Sthree^M$
Connection to host closed.

使用伪终端复制二进制文件
$ ssh -t anthony@remote_host 'cat /usr/bin/free' > ~/free
Connection to remote_host closed.

不使用伪终端复制二进制文件:
$ ssh anthony@remote_host 'cat /usr/bin/free' > ~/free2

两个文件不相同:
$ diff ~/free*
Binary files /home/anthony/free and /home/anthony/free2 differ

使用伪终端复制的一个已损坏:
$ chmod +x ~/free*
$ ./free
Segmentation fault

而另一个未损坏:
$ ./free2
             total       used       free     shared    buffers     cached
Mem:       2065496    1980876      84620          0      48264    1502444
-/+ buffers/cache:     430168    1635328
Swap:      4128760        112    4128648

通过SSH传输文件
这尤其重要适用于使用SSH进行数据传输的程序,例如scprsync。对SCP协议的工作方式的详细说明说明了SCP协议是如何由文本协议消息和二进制文件数据混合而成的。

OpenSSH可以帮助您保护自己不受保护
值得注意的是如果使用了-t标志,则OpenSSH ssh客户端如果检测到其stdin流不是终端,将拒绝分配伪终端:
$ echo testing | ssh -t anthony@remote_host 'echo $TERM'
Pseudo-terminal will not be allocated because stdin is not a terminal.
dumb

您仍然可以强制OpenSSH客户端分配一个伪终端。带有-tt的伪终端:
$ echo testing | ssh -tt anthony@remote_host 'echo $TERM'
xterm

无论哪种情况,(明智地)不在乎是否重定向了stdoutstderr
$ ssh -t anthony@remote_host 'echo $TERM' >| ssh_output
Connection to remote_host closed.


评论


我没有考虑过非常有趣的一点,感谢您添加此答案!

–珍妮D
16年1月14日在14:54

我可以重现结果,但第一个不同的字节不是SSH的控制序列之一,而是\ n已被\ r \ n取代。

–马丁·多雷(Martin Dorey)
20年6月23日在18:33

@MartinDorey谢谢你。我不得不花最后几个小时来研究其他概念证明。 :)我现在已经编辑了答案,以改善它的含义,在分配PTY时会更多地解释不同的字符示例。发现得好!

– Anthony Geoghegan
20年6月24日,0:16

#3 楼

在远程主机上,我们必须使用以下设置:

/etc/sudoers
...
Defaults requiretty


没有sudo

$ ssh -T user@host echo -e 'foo\nbar' | cat -e
foo$
bar$


并带有sudo

$ ssh -T user@host sudo echo -e 'foo\nbar' | cat -e
sudo: sorry, you must have a tty to run sudo


使用sudo可以获得额外的回车符

$ ssh -t user@host sudo echo -e 'foo\nbar' | cat -e
foo^M$
      bar^M$
            Connection to localhost closed.


解决方案是禁用翻译换行到带有stty -onlcr的回车换行符

$ ssh -t user@host stty -onlcr\; sudo echo -e 'foo\nbar' | cat -e
foo$
    bar$
        Connection to localhost closed.


评论


不错,但是输出缩进了/:您是否知道是否可以自动回车(类似unix)?

– oop
18-04-25在7:41

#4 楼

考虑一下向后兼容性。

ssh的2种主要模式是带tty的交互式登录和不带tty的指定命令,因为它们分别是rloginrsh的确切功能。 ssh需要提供rlogin / rsh功能的超集才能成功替代。

因此,默认值是在ssh诞生之前确定的。必须使用新选项访问“我想指定命令并获得tty”之类的组合。很高兴至少现在有了该选项,这与使用rsh时不同。我们并没有牺牲任何有用的功能来获得加密连接。我们有额外的功能!

#5 楼

来自man ssh

 -t      Force pseudo-tty allocation.  This can be used to execute arbi-
         trary 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.


这使您可以将“外壳”放入远程服务器。对于不授予外壳程序访问权限但允许SSH的服务器(即,Github是SFTP访问的示例),使用此标志将导致服务器拒绝您的连接。

外壳程序还具有所有环境变量(例如$PATH),因此执行脚本通常需要使用tty才能工作。

评论


这不能回答问题。我已经知道如何分配伪tty。我想知道为什么我不应该总是分配一个。

– Chas。欧文斯
2014年5月6日14:59

@ Chas.Owens因为,正如我在答案中指出的那样,某些SSH服务器不允许tty访问,如果您从服务器请求一个,它将断开连接。

– Nathan C
2014年5月6日15:10

我认为您可能会混淆一些术语。它不是仅由于与PTY相关联的外壳。通常有三种类型的外壳:非交互式,交互式和登录。登录是其他两种外壳类型的另一个特征。这三个文件的排列决定了登录时将源文件,这又会影响环境的初始化方式。 (变量,正如您所提到的)

–安德鲁(Andrew B)
2014年5月6日下午16:29

@AndrewB你是对的...我也从这个问题中学到了一些东西。 :)

– Nathan C
2014年5月7日0:09