Написання до stdin процесу


10

Наскільки я розумію, якщо я введіть наступне ...

 python -i

... пітон-перекладач тепер буде читати зі stdin, поводячись (очевидно) так:

 >>> print "Hello"
 Hello

Я би очікував, що це зробить те саме, якщо я це роблю:

 echo 'print "Hello"' > /proc/$(pidof python)/fd/0

Але це вихід (маючи фактично порожній рядок):

 >>> print "Hello"
 <empyline>

Це для мене схоже, він просто взяв print "Hello"\nі написав це stdout, але не інтерпретував це. Чому це не працює і що я повинен зробити, щоб це працювало?


Ioctl TIOCSTI може записати в stdin терміналу так, ніби дані були введені з клавіатури. Наприклад github.com/thrig/scripts/blob/master/tty/ttywrite.c
roaima

Відповіді:


9

Таким чином, надсилання даних до оболонок / перекладачів є дуже схильною до проблем та дуже важко працювати будь-яким надійним способом.

Правильний спосіб - це використовувати сокети, тому вони були винайдені, ви можете це зробити в командному рядку, використовуючи ncat ncабо socatприв’язуючи процес python до простого сокета. Або напишіть просту програму python, яка прив’язується до порту та слухає команди для інтерпретації на сокет.

розетки можуть бути локальними та не піддаватися впливу будь-якого веб-інтерфейсу.


Проблема полягає в тому, що якщо ви починаєте pythonз командного рядка, він, як правило, прикріплюється до оболонки, яка приєднана до терміналу, насправді ми можемо бачити

$ ls -al /proc/PID/fd
lrwxrwxrwx 1 USER GROUP 0 Aug 1 00:00 0 -> /dev/pty1

тому, коли ви пишете в stdinpython, ви насправді пишете на ptypsuedo-термінал, який є пристроєм ядра, а не простим файлом. Він використовує ioctlне readта write, тож ви побачите вихід на екрані, але він не буде надісланий до спареного процесу ( python)

Один із способів повторити те, що ви намагаєтесь, це за допомогою fifoабо named pipe.

# make pipe
$ mkfifo python_i.pipe
# start python interactive with pipe input
# Will print to pty output unless redirected
$ python -i < python_i.pipe &
# keep pipe open 
$ sleep infinity > python_i.pipe &
# interact with the interpreter
$ echo "print \"hello\"" >> python_i.pipe

Ви також можете використовувати screenлише для введення даних

# start screen 
$ screen -dmS python python
# send command to input
$ screen -S python -X 'print \"hello\"'
# view output
$ screen -S python -x

Якщо ви тримаєте трубу відкритою (наприклад sleep 300 > python_i.pipe &), інша сторона не закриється і pythonпродовжує приймати команди вниз по трубі. ЄОФ як такого не надсилається echo.
roaima

@roaima ви праві, я помилився, коли зрозумів, що ехо надсилає EOF, коли він закриває потік. Це неможливо уникнути за допомогою |труб, однак правильно?
катастрофа

Я вже був по дорозі фіфо, але echo something > fifoце призвело б до отримання EOF, який зупинить багато додатків. Хоча sleep infinity > fifoвирішення не перейшло мою середину, дякую!
Шеппі

1
насправді продовжуючи свою ідею, ви також можете зробити це, python -i <> fifoщо також запобіжить EOF
Шеппі

10

Доступ не має доступу до дескриптора файлу 0 процесу PID , він отримує доступ до файлу, який PID відкрив у файлі дескриптора 0. Це тонка відмінність, але це має значення. Дескриптор файлу - це з'єднання, яке має процес до файлу. Запис у дескриптор файлу записує у файл незалежно від того, як файл був відкритий./proc/PID/fd/0

Якщо це звичайний файл, його запис змінює файл. Дані не обов'язково будуть читати далі процес: це залежить від позиції, що додається до дескриптора файлу, який використовується для читання файлу. Коли процес відкривається , він отримує той самий файл, що й інший процес, але положення файлів є незалежними./proc/PID/fd/0/proc/PID/fd/0

Якщо це труба, то запис до неї додає дані до буфера труби. У цьому випадку процес, який читається з труби, буде читати дані./proc/PID/fd/0

Якщо це термінал, то запис на нього виводить дані на термінал. Термінальний файл є двонаправленим: запис у нього видає дані, тобто термінал відображає текст; зчитування з терміналу вводить дані, тобто термінал передає введення користувача./proc/PID/fd/0

Python і читає, і записує в термінал. Під час запуску echo 'print "Hello"' > /proc/$(pidof python)/fd/0ви пишете print "Hello"до терміналу. Термінал відображається print "Hello"за інструкцією. Процес python нічого не бачить, він все ще чекає введення.

Якщо ви хочете подати вхід до процесу Python, вам доведеться отримати термінал для цього. Дивіться відповідь катастрофи щодо способів цього зробити.


2

Виходячи з того, що сказав Жилл , якщо ми хочемо написати на stdin процесу, який приєднаний до терміналу, нам насправді потрібно надіслати інформацію до терміналу. Однак, оскільки термінал виступає як формою введення, так і виводу, при записі на нього термінал не може знати, що ви хочете записати в процес, що працює в ньому, а не на "екран".

Однак у Linux є нестандартний спосіб імітації вводу користувача за допомогою запиту ioctl TIOCSTI(управління терміналом вводу / виводу - імітація термінального вводу), який дозволяє нам надсилати символи до терміналу так, як ніби вони були введені користувачем.

Я лише поверхово усвідомлюю, як це працює, але виходячи з цієї відповіді, слід зробити це чимось за принципом

import fcntl, sys, termios

tty_path = sys.argv[1]

with open(tty_path, 'wb') as tty_fd:
    for line in sys.stdin.buffer:
        for byte in line:
            fcntl.ioctl(tty_fd, termios.TIOCSTI, bytes([byte]))

Деякі зовнішні ресурси:

http://man7.org/linux/man-pages/man2/ioctl.2.html

http://man7.org/linux/man-pages/man2/ioctl_tty.2.html


Зауважте, що питання не стосується будь-якої конкретної операційної системи, і що TIOCSTI не походить з Linux. Майже за два роки до написання цієї відповіді люди почали відмовлятися від TIOCSTI з міркувань безпеки. unix.stackexchange.com/q/406690/5132
JdeBP

@JdeBP Звідси моє зазначення "Linux" (хоча я не впевнений, звідки він виник). А під "людьми", здається, ви маєте на увазі якісь BSD? З того, що я читав тому, коли писав це, схоже, що в набагато старішому здійсненні існував ризик безпеки, який з того часу був виправлений, але BSD все-таки вважав, що "безпечніше" взагалі скинути йоктл. Однак я дуже незнайомий ні з чим, тому я подумав, що краще не говорити, що щось не можливо в певних системах, коли я не маю досвіду роботи з цією системою.
Christian Reall-Fluharty
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.