为什么向subprocess.Popen提供stdin会导致stdout输出变化?
我正在使用Python的subprocess.Popen来进行一些FTP操作,使用的是主机操作系统的二进制客户端。由于各种原因,我不能使用ftplib或其他库。
当我给Popen实例添加一个stdin处理器时,二进制客户端的行为似乎会发生变化。例如,使用Windows XP的ftp客户端,它可以接受一个命令文本文件:
>>>from subprocess import Popen, PIPE
>>>p = Popen(['ftp','-A','-s:commands.txt','example.com'], stdout=PIPE)
>>>p.communicate()[0]
'Connected to example.com.
220 ProFTPD 1.3.1 Server (Debian) ...
331 Anonymous login ok, send your complete email address as your password
<snip>
ftp> binary
200 Type set to I
ftp> get /testfiles/100.KiB
200 PORT command successful
150 Opening BINARY mode data connection for /testfiles/100.KiB (102400 bytes)
226 Transfer complete
ftp: 102400 bytes received in 0.28Seconds 365.71Kbytes/sec.
ftp> quit
>>>
commands.txt:
binary
get /testfiles/100.KiB
quit
当同时提供stdin时,stdout中只会显示:
>>>from subprocess import Popen, PIPE
>>>p = Popen(['ftp','-A','-s:commands.txt','example.com'], stdin=PIPE, stdout=PIPE)
>>>p.communicate()[0]
'binary
get /testfiles/100.KiB
quit'
>>>
起初我以为这是XP ftp客户端的一个怪癖,可能是因为它知道自己不在交互模式下,所以限制了输出。然而,在OS X的ftp中也出现了同样的情况——如果提供了stdin,stdout中就会缺少所有服务器的响应,这让我觉得这可能是正常现象。
在Windows上,我可以使用-s选项来有效地脚本化ftp,而不需要使用stdin,但在其他平台上,就得依赖shell来进行这种交互。
在这两个平台上,Python的版本都是2.6.x。为什么提供一个stdin的处理器会改变stdout的输出,服务器的响应又去了哪里呢?
2 个回答
这个程序可能在用 isatty(3)
来检查标准输入(stdin)上是否有一个终端设备(tty)。
我记得在某个地方看到过(但不记得具体在哪里),说Windows的ftp客户端是源自早期的BSD实现。所以它肯定和Mac OS X的ftp实现有一些关系。
对我来说,这个问题和Popen没有关系,而是和ftp客户端程序的实现有关。这个程序会检查它被启动的环境(看看是和人互动还是和一个脚本互动),使用的是isatty(3),就像Ignacio在他的回答中提到的那样。这是一个常见的做法,适用于可以在这两种环境中使用的程序。一个大家都知道的例子是GNU grep的实现,它的--color=auto选项:只有当输出是一个终端时,它才会给输出加颜色,如果grep的输出被传递到另一个命令,就不会加颜色。