偶尔我会做类似的事情
ssh user@host sudo thing
并提醒我ssh默认情况下不分配伪tty。为什么不呢?如果我使用别名ssh
,将会失去什么好处ssh -t
?
ssh -t
始终运行很糟糕,因为它可能导致某些命令以奇怪的方式中断。而运行不需要PTY的命令将导致产生清晰的错误消息,提示您需要终端。
偶尔我会做类似的事情
ssh user@host sudo thing
并提醒我ssh默认情况下不分配伪tty。为什么不呢?如果我使用别名ssh
,将会失去什么好处ssh -t
?
ssh -t
始终运行很糟糕,因为它可能导致某些命令以奇怪的方式中断。而运行不需要PTY的命令将导致产生清晰的错误消息,提示您需要终端。
Answers:
主要区别在于交互性的概念。这类似于在脚本内部本地运行命令,而不是自己键入命令。区别在于,远程命令必须选择默认值,并且非交互式最安全。(通常最诚实)
Ctrl-c
中断通常会导致ssh命令上的循环立即中断,但是您的控制序列将被发送到远程服务器。这导致需要“敲击”击键以确保它在控制离开 ssh命令时,但在下一个ssh命令开始之前到达。我警告不要ssh -t
在无人看管的脚本中使用,例如crons。非交互式外壳程序要求远程命令以交互方式进行输入操作,这会引起各种麻烦。
您还可以在自己的Shell脚本中测试终端的存在。要使用较新版本的bash测试STDIN:
# fd 0 is STDIN
[ -t 0 ]; echo $?
ssh
时ssh -t
,您可以期望在行尾获得额外的回车符。它可能对您不可见,但是在那里。它会显示为^M
管道连接时的形式cat -e
。然后,您必须付出额外的努力,以确保不会将此控制代码分配给变量,特别是如果要将输出插入数据库中时,尤其如此。这是与之前相同的bash测试,但适用于STDOUT:
# fd 1 is STDOUT
[ -t 1 ]; echo $?
尽管可以解决这些问题,但您不可避免地会忘记围绕它们设计脚本。我们所有人都在某个时候做。您的团队成员可能也没有意识到/记住此别名,这会在他们编写使用您的别名的脚本时给您带来麻烦。
混叠ssh
到ssh -t
非常多的地方,你会被违反的设计原则的情况下,至少惊喜 ; 人们将遇到他们未曾预料到的问题,并且可能不了解造成这些问题的原因。
还没有在其他的答案被提及一个优点是,工作时没有一个伪终端,所述SSH 转义字符如~C
被不支持 ; 这使得程序可以安全地传输可能包含这些序列的二进制文件。
使用伪终端复制二进制文件:
$ 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
这对于诸如scp
或rsync
使用SSH进行数据传输的程序特别重要。此的SCP协议如何工作的详细描述中解释了SCP协议如何由文本协议消息和二进制文件数据的混合物。
值得注意的是,即使使用了该-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
在这两种情况下,(合理)不关心stdout
或stderr
重定向:
$ ssh -t anthony@remote_host 'echo $TERM' >| ssh_output
Connection to remote_host closed.
在远程主机上,我们必须使用以下设置:
/etc/sudoers
...
Defaults requiretty
没有须藤
$ ssh -T user@host echo -e 'foo\\nbar' | cat -e
foo$
bar$
和与须藤
$ 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.
考虑向后兼容性。
ssh的两种主要模式是带tty的交互式登录和不带tty的指定命令,因为它们分别是rlogin
和的确切功能rsh
。ssh需要提供rlogin
/ rsh
功能的超集才能成功替代。
因此,默认值是在ssh诞生之前确定的。必须使用新选项访问“我想指定命令并获取tty”之类的组合。很高兴,至少我们有这个选项现在,不像当我们使用rsh
。我们并没有牺牲任何有用的功能来获得加密连接。我们有额外的功能!
来自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才能起作用。
non-interactive
,interactive
,和login
。login
是其他两种外壳类型的另一个特征。这三个文件的排列决定了登录时将源文件,这又会影响环境的初始化方式。(变量,正如您所提到的)