2010-03-01 4 views
6

Я использую подпроцесс Python.Popen для выполнения FTP, используя двоичный клиент операционной системы хоста. Я не могу использовать ftplib или любую другую библиотеку по разным причинам.Почему доставка stdin на subprocess.Popen вызывает то, что записано в stdout, чтобы изменить?

Поведение двоичного файла, похоже, изменится, если я присоединяю обработчик stdin к экземпляру Popen. Например, с помощью FTP-клиента XP, которая принимает текстовый файл команд выпуска:

>>>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, возможно, зная, что он не находится в интерактивном режиме и поэтому ограничивает его выход. Однако такое же поведение происходит с ftp OS X - все ответы сервера отсутствуют в stdout, если поставляется stdin, что заставляет меня думать, что это нормальное поведение.

В Windows я могу использовать -s-коммутатор для эффективного сценария ftp без использования stdin, но на других платформах для оболочки используется один и тот же интерфейс.

Python версия 2.6.x на обеих платформах. Зачем нужно указывать дескриптор stdin для смены stdin и на какие ответы сервера?

+0

Рассматривали ли вы использование 'ftplib'? http://docs.python.org/library/ftplib.html –

+0

Каковы причины, по которым вы не можете использовать ftplib. он поставляется с вашим дистрибутивом Python? – ghostdog74

ответ

4

Я думаю, что я где-то читал (но не могу вспомнить, где), что клиент ftp Windows пришел из одной из исходных реализаций BSD. В этом он наверняка разделяет некоторые отношения с реализацией ftp Mac OS X.

Для меня это не относится к Popen, а к реализации программы ftp-клиента клиента, что делает некоторые проверки контекста, в котором он запущен (чтобы увидеть, взаимодействует ли он с человеком или скриптом оболочки), используя isatty (3), как упоминалось в Игнасио в его ответе. Это обычная практика для программ, которые могут использоваться в обоих контекстах. Известным примером является реализация grep grep для опции --color = auto: она будет раскрашивать вывод только в том случае, если stdout является tty, а не если вывод grep передан в другую команду.

+0

Да, я думаю, вы правы в этом. Когда я запускаю рабочий код в качестве службы Windows (который также имеет неинтерактивный stdin), я получаю такое же поведение. Итак, следующий вопрос: существует ли кросс-платформенный способ «обманывать» исполняемый файл, что он разговаривает с tty? Я предполагаю, что на самом деле не нужны какие-либо функции tty для получения полной информации. – Matt

+0

Не то, чтобы я знал. Однако обратите внимание, что нет стандартной кросс-платформенной ftp-команды с унифицированным интерфейсом (т. Е. Аргументы командной строки), поэтому вам придется записывать переносимый код с таким подходом. Если ваш сценарий необходимо только загрузить несколько файлов с помощью FTP, я рекомендую использовать URLLIB или urllib2: импорт URLLIB е = urllib.urlopen («ftp://example.com/testfiles/100.KiB») data = f.read() –

+0

Я провел некоторое время, экспериментируя с pexpect/wexpect, который обещал быть достаточно независимым от платформы решением.К сожалению, wexpect нуждается в консоли и умеет умереть, когда запускается как служба Windows. – Matt

7

Программа может использовать isatty(3), чтобы обнаружить наличие tty на stdin.