Почему передача stdin в subprocess.Popen приводит к изменению того, что написано в stdout?

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

Поведение двоичного файла, похоже, изменится, если я присоединю обработчик стандартного ввода к экземпляру 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  
>>>

команды.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'  
>>>

Первоначально я думал, что это особенность ftp-клиента XP, возможно, зная, что он не находится в интерактивном режиме, и, следовательно, ограничивая его вывод. Однако то же самое происходит с ftp OS X - все ответы сервера отсутствуют в стандартном выводе, если предоставлен стандартный ввод - что заставляет меня думать, что это нормальное поведение.

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

Версия Python 2.6.x на обеих платформах. Почему предоставление дескриптора для стандартного ввода изменило стандартный вывод и куда ушли ответы сервера?


person Matt    schedule 01.03.2010    source источник
comment
Рассматривали ли вы возможность использования ftplib? docs.python.org/library/ftplib.html   -  person Ignacio Vazquez-Abrams    schedule 01.03.2010
comment
по каким причинам вы не можете использовать ftplib. он поставляется с вашим дистрибутивом Python, верно?   -  person ghostdog74    schedule 01.03.2010


Ответы (2)


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

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

person gurney alex    schedule 01.03.2010
comment
Да, я думаю, что вы правы насчет этого. Когда я запускаю рабочий код как службу Windows (которая также имеет неинтерактивный стандартный ввод), я получаю такое же поведение. Итак, следующий вопрос: существует ли кросс-платформенный способ «обмануть» исполняемый файл, чтобы он разговаривал с tty? Я предполагаю, что на самом деле не нужны какие-либо функции tty для получения полного вывода. - person Matt; 02.03.2010
comment
Не то, что я знаю из. Однако обратите внимание, что не существует стандартной межплатформенной ftp-команды с унифицированным интерфейсом (т. е. аргументами командной строки), поэтому при таком подходе вам будет сложно писать переносимый код. Если вашему сценарию нужно загрузить только пару файлов по ftp, я рекомендую использовать urllib или urllib2: rel="nofollow noreferrer">example.com/testfiles/100.KiB') data = f.read() - person gurney alex; 03.03.2010
comment
Я провел некоторое время, экспериментируя с pexpect/wexpect, которые обещали быть достаточно независимым от платформы решением. К сожалению, wexpect нуждается в консоли и ужасно умирает при запуске в качестве службы Windows. - person Matt; 03.03.2010

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

person Ignacio Vazquez-Abrams    schedule 01.03.2010