Viết cho stdin của một quá trình


10

Theo như tôi hiểu nếu tôi gõ như sau ...

 python -i

... trình thông dịch python bây giờ sẽ đọc từ stdin, cư xử (rõ ràng) như thế này:

 >>> print "Hello"
 Hello

Tôi hy vọng nó sẽ làm điều tương tự nếu tôi làm điều này:

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

Nhưng đây là đầu ra (ghi một dòng trống thực tế):

 >>> print "Hello"
 <empyline>

Điều này với tôi có vẻ như, nó chỉ lấy print "Hello"\nvà viết nó stdout, nhưng không giải thích nó. Tại sao nó không hoạt động và tôi phải làm gì để nó hoạt động?


TIOCSTI ioctl có thể ghi vào stdin của thiết bị đầu cuối như thể dữ liệu đã được nhập từ bàn phím. Ví dụ: github.com/thrig/scripts/blob/master/tty/ttywrite.c
roaima

Câu trả lời:


9

Gửi đầu vào cho shell / trình thông dịch theo cách này rất dễ gặp vấn đề và rất khó để làm việc theo bất kỳ cách đáng tin cậy nào.

Cách thích hợp là sử dụng socket, đây là lý do tại sao chúng được phát minh, bạn có thể thực hiện điều này trong dòng lệnh bằng cách sử dụng ncat nchoặc socatliên kết một quá trình python với một socket đơn giản. Hoặc viết một ứng dụng python đơn giản liên kết với cổng và lắng nghe các lệnh để diễn giải trên một socket.

ổ cắm có thể là cục bộ và không tiếp xúc với bất kỳ giao diện web.


Vấn đề là nếu bạn bắt đầu pythontừ dòng lệnh, nó thường được gắn vào vỏ của bạn được gắn vào một thiết bị đầu cuối, trên thực tế chúng ta có thể thấy

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

Vì vậy, khi bạn viết thư cho stdinpython, bạn thực sự đang ghi vào ptythiết bị đầu cuối psuedo, đây là một thiết bị kernel, không phải là một tệp đơn giản. Nó ioctlkhông sử dụng readwritevì vậy bạn sẽ thấy đầu ra trên màn hình của mình, nhưng nó sẽ không được gửi đến quá trình sinh ra ( python)

Một cách để tái tạo những gì bạn đang cố gắng là với một fifohoặc 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

Bạn cũng chỉ có thể sử dụng screencho đầu vào

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

Nếu bạn giữ đường ống mở (ví dụ sleep 300 > python_i.pipe &), phía bên kia sẽ không đóng và pythonsẽ tiếp tục chấp nhận các lệnh xuống đường ống. Không có EOF như vậy được gửi bởi echo.
roaima

@roaima bạn nói đúng, tôi đã nhầm lẫn khi hiểu rằng echo gửi EOF khi nó đóng luồng. Điều này là không thể tránh khỏi với |đường ống, tuy nhiên, đúng không?
crasic

Tôi đã xuống đường fifo, nhưng echo something > fifosẽ khiến nó nhận được EOF, thứ sẽ dừng nhiều ứng dụng. Cách sleep infinity > fifogiải quyết không vượt qua giữa tôi, cảm ơn bạn!
Sheppy

1
thực sự tiếp tục ý tưởng của bạn, bạn cũng có thể thực hiện điều python -i <> fifođó cũng sẽ ngăn chặn EOF
Sheppy

10

Truy cập không truy cập bộ mô tả tệp 0 của quá trình PID , nó truy cập vào tệp mà PID đã mở trên bộ mô tả tệp 0. Đây là một sự phân biệt tinh tế, nhưng nó quan trọng. Một mô tả tập tin là một kết nối mà một quá trình có một tập tin. Viết vào một mô tả tập tin ghi vào tệp bất kể tập tin đã được mở như thế nào./proc/PID/fd/0

Nếu là một tệp thông thường, ghi vào nó sẽ sửa đổi tệp. Dữ liệu không nhất thiết là quy trình sẽ đọc gì tiếp theo: nó phụ thuộc vào vị trí được đính kèm với bộ mô tả tệp mà quy trình đang sử dụng để đọc tệp. Khi một quy trình mở ra , nó sẽ nhận được cùng một tệp với quy trình khác, nhưng các vị trí tệp là độc lập./proc/PID/fd/0/proc/PID/fd/0

Nếu là một đường ống, sau đó ghi vào nó sẽ nối dữ liệu vào bộ đệm của đường ống. Trong trường hợp đó, quá trình đọc từ đường ống sẽ đọc dữ liệu./proc/PID/fd/0

Nếu là một thiết bị đầu cuối, sau đó ghi vào nó để xuất dữ liệu trên một thiết bị đầu cuối. Một tệp thiết bị đầu cuối là hai chiều: ghi vào nó xuất dữ liệu, tức là thiết bị đầu cuối hiển thị văn bản; đọc từ một thiết bị đầu cuối nhập dữ liệu, tức là thiết bị đầu cuối truyền đầu vào của người dùng./proc/PID/fd/0

Python vừa đọc vừa ghi vào terminal. Khi bạn chạy echo 'print "Hello"' > /proc/$(pidof python)/fd/0, bạn đang viết print "Hello"cho thiết bị đầu cuối. Thiết bị đầu cuối hiển thị print "Hello"theo hướng dẫn. Quá trình python không thấy gì, nó vẫn đang chờ đầu vào.

Nếu bạn muốn cung cấp đầu vào cho quy trình Python, bạn phải lấy thiết bị đầu cuối để thực hiện. Xem câu trả lời của crasic để biết cách làm điều đó.


2

Dựa trên những gì Gilles đã nói , nếu chúng ta muốn viết cho stdin của một quy trình được gắn vào một thiết bị đầu cuối, chúng ta thực sự cần phải gửi thông tin đến thiết bị đầu cuối. Tuy nhiên, vì thiết bị đầu cuối đóng vai trò là một dạng đầu vào cũng như đầu ra, nên khi ghi vào thiết bị đầu cuối, thiết bị đầu cuối không có cách nào để biết rằng bạn muốn ghi vào một quá trình chạy trong nó chứ không phải là "màn hình".

Tuy nhiên, Linux có một cách mô phỏng đầu vào của người dùng thông qua một yêu cầu ioctl được gọi là TIOCSTI(Điều khiển đầu vào I / O - Mô phỏng đầu vào đầu cuối) cho phép chúng ta gửi các ký tự đến một thiết bị đầu cuối như thể chúng được người dùng nhập vào.

Tôi chỉ nhận thức một cách hời hợt về cách thức hoạt động của nó, nhưng dựa trên câu trả lời này , có thể làm điều này với một cái gì đó dọc theo dòng

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]))

Một số tài nguyên bên ngoài:

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

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


Quan sát rằng câu hỏi không dành riêng cho bất kỳ hệ điều hành cụ thể nào và TIOCSTI không bắt nguồn từ Linux. Gần hai năm trước khi câu trả lời này được viết, mọi người bắt đầu bỏ TIOCSTI vì lý do bảo mật. unix.stackexchange.com/q/406690/5132
JdeBP

@JdeBP Do đó tôi chỉ định "Linux" (mặc dù tôi không chắc nó bắt nguồn từ đâu). Và bởi "người", có vẻ như bạn có nghĩa là một số BSD? Từ những gì tôi đọc lại khi tôi viết bài này, có vẻ như, trong một triển khai cũ hơn nhiều, rủi ro bảo mật đã được vá, nhưng BSD vẫn cho rằng "an toàn" hơn khi bỏ ioctl hoàn toàn. Tuy nhiên tôi rất lạ lẫm với bất kỳ điều gì trong số này, vì vậy tôi nhận ra rằng tốt hơn hết là không nói rằng một số hệ thống không thể thực hiện được khi tôi không có kinh nghiệm với hệ thống đó.
Christian Reall-Fluharty
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.