Skip to main content

与远程程序的交互

Fabric的主要操作(runsudo)能够以与 ssh 程序几乎相同的方式向远程端发送本地输入。例如,显示密码提示(例如数据库转储实用程序或更改用户密码)的程序将表现得就像直接与其进行交互一样。

然而,与 ssh 本身一样,Fabric的此功能的实现受到一些不总是直观的限制。本文详细讨论了这些问题。

注解

不熟悉Unix stdout和stderr管道和/或终端设备基础的读者可能希望分别访问 Unix管道伪终端 的维基百科页面。

结合stdout和stderr

要注意的第一个问题是stdout和stderr流,以及为什么它们根据需要分开或组合。

缓冲

Fabric 0.9.x和更早版本,以及Python本身,逐行缓冲输出:在找到换行符之前,文本不会打印到用户。这在大多数情况下工作正常,但是当需要处理局部线输出(如提示)时变得有问题。

注解

行缓冲输出可以使程序看起来停止或冻结,没有任何理由,因为提示打印没有换行符的文本,等待用户输入他们的输入,然后按返回。

较新的Fabric版本在逐个字符的基础上缓冲输入和输出,以使与提示的交互成为可能。这具有方便的副作用,使得能够利用“curses”库或否则重绘屏幕与复杂程序交互(认为 top)。

穿越溪流

不幸的是,同时打印到stderr和stdout(与许多程序一样)意味着当两个流每次独立地打印一个字节时,它们可能变为乱码或网格在一起。虽然有时可以通过线路缓冲一个流而不是其他流来缓解,但它仍然是一个严重的问题。

为了解决这个问题,Fabric在我们的SSH层中使用一个设置,它将两个流合并在一个低层,并使输出更自然地出现。此设置在Fabric中表示为 combine_stderr env var和keyword参数,默认情况下为 True

由于此默认设置,输出将正确显示,但以 run/sudo 的返回值上的空 .stderr 属性为代价,因为所有输出将显示为stdout。

相反,需要在Python级别使用不同的stderr流并且不被乱码的面向用户的输出(或者从有问题的命令中隐藏stdout和stderr的人)打扰的用户可以选择根据需要将其设置为 False

伪终端

向用户呈现交互式提示时要考虑的另一个主要问题是回应用户自己的输入。

回声

典型的终端应用或真实文本终端(例如,当使用没有运行GUI的Unix系统时)使用被称为tty或pty(用于伪终端)的终端设备来呈现节目。这些会自动将所有输入的文本回传给用户(通过stdout),因为交互时不会看到刚刚输入的内容很难。终端设备还能够有条件地关闭回波,允许安全的密码提示。

但是,程序可以在没有tty或pty存在的情况下运行(例如考虑cron作业),在这种情况下,送入程序的任何stdin数据都不会被回显。这对于在没有任何人的情况下运行的程序是合乎需要的,并且它也是Fabric的旧的默认操作模式。

面料的方法

不幸的是,在通过Fabric执行命令的上下文中,当没有pty用于回显用户的stdin时,Fabric必须为它们回显它。这对于许多应用程序是足够的,但它提出了密码提示的问题,这变得不安全。

为了安全和满足最小惊喜的原则(在用户通常期望事情表现为他们在终端模拟器中运行时),Fabric 1.0和更大的默认强制pty。启用pty后,Fabric简单地允许远程端处理回显或隐藏stdin,并且不会回显任何内容。

注解

除了允许正常的回声行为之外,pty还意味着当连接到终端设备时行为不同的程序将这样做。例如,对终端上的输出进行着色的程序,而不是在后台运行的程序将打印彩色输出。如果您检查 runsudo 的返回值,请小心这一点!

对于需要关闭pty行为的情况,可以使用 --no-pty 命令行参数和 always_use_pty env var。

结合两者

作为最后一点,请记住,使用伪终端有效地意味着结合stdout和stderr - 与 combine_stderr 设置的方式大致相同。这是因为终端设备自然地将stdout和stderr两者发送到相同的地方 - 用户的显示器,从而使得不可能在它们之间进行区分。

然而,在Fabric级别,两组设置彼此不同,并且可以以各种方式组合。默认值为两者都设置为 True;其他组合如下:

  • run("cmd", pty=False, combine_stderr=True):将导致Fabric回显所有stdin本身,包括密码,以及潜在地改变 cmd 的行为。如果 cmd 在pty下运行时会出现不希望出现的情况,并且您不关心密码提示,这将非常有用。

  • run("cmd", pty=False, combine_stderr=False):使用两个设置 False,Fabric将回送stdin,并且不会发出pty - 这很可能导致除了最简单的命令之外的所有的不希望的行为。然而,它也是访问一个不同的stderr流的唯一方法,这是偶尔有用的。

  • run("cmd", pty=True, combine_stderr=False):有效,但不会真正有很大的区别,因为 pty=True 仍然会导致合并流。可能有助于避免 combine_stderr 中的任何边缘情况问题(目前没有已知的)。