TL; DR
不要使用-t。-t涉及远程主机上的伪终端,仅应用于从终端运行可视应用程序。
说明
换行符(也称为换行符或\n)是发送到终端时告诉终端将其光标向下移动的字符。
但是,当您seq 3在终端中运行时,即在其中seq写入1\n2\n3\n类似的内容/dev/pts/0,您不会看到:
1
2
3
但
1
2
3
这是为什么?
实际上,当seq 3(或ssh host seq 3为此)书写时1\n2\n3\n,终端会看到1\r\n2\r\n3\r\n。即,换行已转换为回车(终端将光标移回屏幕左侧)并换行。
这是由终端设备驱动程序完成的。更确切地说,根据终端(或伪终端)设备的线路规范,驻留在内核中的软件模块。
您可以使用stty命令来控制该行规程的行为。LF-> 的翻译用CRLF打开
stty onlcr
(通常默认情况下启用)。您可以使用以下方法将其关闭:
stty -onlcr
或者,您可以使用以下命令关闭所有输出处理:
stty -opost
如果执行该操作并运行seq 3,则将看到:
$ stty -onlcr; seq 3
1
2
3
如预期的那样。
现在,当您这样做时:
seq 3 > some-file
seq不再写入终端,而是写入文件,无需进行翻译。所以some-file确实包含1\n2\n3\n。仅在写入终端设备时才执行转换。而且仅用于显示。
同样,当您这样做时:
ssh host seq 3
ssh正在写,1\n2\n3\n而不管ssh输出到什么。
实际发生的是该seq 3命令在host其标准输出重定向到管道的情况下运行。ssh主机上的服务器读取管道的另一端,并通过加密通道将其发送到ssh客户端,然后ssh客户端将其写入其stdout中(在您的情况下是伪终端设备),将LFs转换为此值以CRLF进行显示。
当标准输出不是终端时,许多交互式应用程序的行为会有所不同。例如,如果您运行:
ssh host vi
vi不喜欢它,不喜欢它的输出到管道。它认为它不是在与能够理解例如光标定位转义序列的设备通信。
因此ssh可以-t选择。使用该选项,主机上的ssh服务器将创建一个伪终端设备,并使stdout(以及stdin和stderr)成为vi。什么vi写道,终端装置通过该远程伪终端线路规则并且由所读出的ssh服务器和发送过来的加密的信道的ssh客户机。除了使用管道代替之外,ssh服务器使用伪终端,与之前相同。
另一个区别是,在客户端,ssh客户端将终端设置为raw模式。这意味着在那里没有完成翻译(opost被禁用以及其他输入端行为)。例如,当您键入时,该字符被发送到远程端Ctrl-C,而不是打断ssh该^C字符,远程伪终端的线路规则将中断发送到远程命令。
当您这样做时:
ssh -t host seq 3
seq 3写入1\n2\n3\n其stdout,这是一个伪终端设备。由于onlcr,它会在主机上1\r\n2\r\n3\r\n转换为加密通道并通过加密通道发送给您。在您这一边,没有翻译(已onlcr禁用),因此1\r\n2\r\n3\r\n未显示(由于该raw模式),并且在终端仿真器的屏幕上正确显示。
现在,如果您这样做:
ssh -t host seq 3 > some-file
与上面没有区别。ssh会写同样的东西:1\r\n2\r\n3\r\n,但是这次变成了some-file。
因此,基本上所有的LF输出seq都已转换CRLF为some-file。
如果执行以下操作,则相同:
ssh -t host cat remote-file > local-file
所有LF字符(0x0a字节)都将转换为CRLF(0x0d 0x0a)。
这可能是文件损坏的原因。对于第二个较小的文件,恰好该文件不包含0x0a字节,因此没有损坏。
请注意,使用不同的tty设置可能会导致不同类型的损坏。与之相关的另一种潜在损坏类型-t是,您在host(~/.bashrc,~/.ssh/rc...)上的启动文件将内容写入其stderr,因为-t远程外壳程序的stdout和stderr最终合并到了sshstdout中(它们都进入了伪指令)。 -终端设备)。
您不希望遥控器cat输出到那里的终端设备。
你要:
ssh host cat remote-file > local-file
您可以这样做:
ssh -t host 'stty -opost; cat remote-file` > local-file
这将起作用(除了上面讨论的stderr损坏案例中的写法之外),但是即使那样也将是次优的,因为您将在上运行不必要的伪终端层host。
更有趣:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
好。
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF 翻译成 CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
再次确定。
$ ssh -t localhost 'stty olcuc; echo x'
X
这是终端线路部门可以完成的另一种输出后处理形式。
$ echo x | ssh -t localhost 'stty -opost; echo' | od -tx1
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Inappropriate ioctl for device
0000000 0a
0000001
ssh当其自己的输入不是终端时,拒绝告诉服务器使用伪终端。您可以通过以下方式强制执行-tt:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
线规在输入方面做得更多。
在这里,echo既不读取其输入,也不要求其输出,x\r\n\n那么它是从哪里来的呢?这是echo远程伪终端(stty echo)的本地地址。所述ssh服务器被供给x\n它从客户端读取到远程伪终端的主侧。并且该行的规则将其回显(在stty opost运行之前,这就是为什么我们看到a CRLF和not的原因LF)。这与远程应用程序是否从stdin读取任何内容无关。
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
的0x3字符被回送为^C(^和C),因为stty echoctl和壳和睡眠接收SIGINT因为stty isig。
因此,当:
ssh -t host cat remote-file > local-file
够糟糕的,但是
ssh -tt host 'cat > remote-file' < local-file
以另一种方式传输文件会更糟。你会得到一些CR - > LF翻译,而且问题的所有特殊字符(^C,^Z,^D,^?,^S...),也是远程cat不会EOF看到,当年底local-file达到,只有当^D被后发送\r,\n或其他^D喜欢做的时候cat > file在你的终端。
-t选项,它会中断传输。除非出于特殊原因需要它们,否则不要使用-t或-T。默认值在绝大多数情况下都适用,因此很少需要这些选项。