2010-03-01 60 views
6

我使用Python的subprocess.Popen使用主機操作系統的二進制客戶端來執行一些FTP。出於各種原因,我無法使用ftplib或任何其他庫。爲什麼提供stdin給subprocess.Popen會導致寫入stdout的內容發生變化?

如果我將一個stdin處理程序附加到Popen實例,二進制的行爲似乎會改變。例如,使用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 

當還提供標準輸入,你在標準輸出得到的是:

>>>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開關在不使用stdin的情況下有效地腳本ftp,但在其他平臺上依賴shell來進行這種交互。

兩種平臺上的Python版本是2.6.x。爲什麼要爲stdin提供stdout的句柄,以及服務器響應的位置在哪裏?

+0

你有沒有考慮過使用'ftplib'? http://docs.python.org/library/ftplib.html – 2010-03-01 14:31:24

+0

你無法使用ftplib的原因是什麼?它帶有你的Python發行版嗎? – ghostdog74 2010-03-01 14:33:53

回答

4

我想我讀了某處(但不記得在哪裏)Windows ftp客戶端來自其中一個原始BSD實現。因爲它肯定會與Mac OS X的ftp實現共享一些關係。

對我來說,這與Popen無關,但與客戶端ftp程序的實現有關,它對啓動的上下文進行一些檢查(查看它是否與人類或shell腳本交互),使用isatty (3)在伊格納西奧的回答中提到。這是在兩種情況下都可以使用的程序的常用做法。一個衆所周知的例子是用於--color = auto選項的GNU grep實現:只有stdout是tty,它纔會着色輸出,而不是grep的輸出被傳送到另一個命令。

+0

是的,我認爲你是對的。當我將工作代碼作爲Windows服務(它也有一個非交互式標準輸入)運行時,我會得到相同的行爲。所以後續問題是 - 是否存在一種跨平臺的方式來「欺騙」它正在與tty通話的可執行文件?我假設它實際上並不需要任何tty的特性來產生完整的輸出。 – Matt 2010-03-02 10:34:23

+0

不是我所知道的。但是請注意,沒有統一接口的標準跨平臺ftp命令(即命令行參數),因此使用此方法編寫可移植代碼很困難。 如果你的腳本只需要下載一對夫婦使用FTP的文件,我建議使用的urllib或urllib2的: 進口的urllib F =了urllib.urlopen(「ftp://example.com/testfiles/100.KiB」 ) data = f.read() – 2010-03-03 06:58:13

+0

我花了一些時間試驗pexpect/wexpect,它承諾是一個合理的平臺獨立解決方案。令人遺憾的是,wexpect需要一個控制檯,並在作爲Windows服務運行時死亡慘重。 – Matt 2010-03-03 14:28:49

7

程序可能使用isatty(3)來檢測stdin上是否存在tty。

相關問題