프로세스의 표준에 쓰기


10

내가 다음을 입력하면 이해하는 한 ...

 python -i

... python-interpreter는 이제 stdin에서 다음과 같이 동작합니다 (분명히).

 >>> print "Hello"
 Hello

내가 이렇게하면 같은 일을 할 것으로 기대합니다.

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

그러나 이것은 출력입니다 (실제 빈 줄).

 >>> print "Hello"
 <empyline>

나에게 이것은 다음과 같이 보였습니다. print "Hello"\n에 쓰고 썼지 만 stdout해석하지 않았습니다. 왜 작동하지 않고 작동하게하려면 어떻게해야합니까?


TIOCSTI ioctl은 마치 키보드에서 데이터가 입력 된 것처럼 터미널의 표준 입력에 쓸 수 있습니다 . 예를 들어 github.com/thrig/scripts/blob/master/tty/ttywrite.c
roaima

답변:


9

이런 식으로 쉘 / 인터프리터에 입력을 보내는 것은 문제가 발생하기 쉽고 신뢰할 수있는 방식으로 작업하기가 매우 어렵습니다.

올바른 방법은 소켓을 사용하는 것입니다. 이것이 소켓이 발명 된 이유입니다. 명령 행에서 ncat nc또는 socat파이썬 프로세스를 간단한 소켓에 바인드하여 바인딩 할 수 있습니다 . 또는 포트에 바인딩하고 소켓에서 해석하는 명령을 수신하는 간단한 Python 애플리케이션을 작성하십시오.

소켓은 로컬 일 수 있으며 웹 인터페이스에 노출되지 않습니다.


문제python명령 줄에서 시작하면 일반적으로 터미널에 연결된 셸에 연결되어 실제로 볼 수 있다는 것입니다

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

stdin파이썬으로 쓸 때 실제로 pty는 단순한 파일이 아니라 커널 장치 인 의사 터미널에 쓰는 것 입니다. ioctlnot 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파이프 아래로 명령이 계속 적용됩니다. 에서 보낸 EOF가 없습니다 echo.
roaima

@roaima 당신이 옳습니다. 나는 에코가 스트림을 닫을 때 EOF를 보내는 것으로 이해했습니다. |그러나 파이프로 는 피할 수 없지만 맞습니까?
crasic

나는 이미 fifo 길 echo something > fifo을 가고 있었지만 많은 응용 프로그램을 중단시킬 수있는 EOF를 얻었습니다. sleep infinity > fifo해결하지만 내 중간 교차하지 않았다, 감사합니다!
Sheppy

1
실제로 당신의 생각을 계속하면, 당신은 또한 python -i <> fifoEOF를 막을 수 있습니다
Sheppy

10

액세스 하면 프로세스 PID 의 파일 디스크립터 0에 액세스 하지 않고 PID 가 파일 디스크립터 0에서 연 파일에 액세스 합니다. 이는 미묘한 차이이지만 중요합니다. 파일 디스크립터는 프로세스가 파일에 연결하는 것입니다. 파일 디스크립터에 쓰기는 파일이 열린 방법에 관계없이 파일에 씁니다./proc/PID/fd/0

일반 파일 인 경우 파일에 쓰면 파일이 수정됩니다. 데이터는 프로세스가 다음에 읽을 내용 일 필요는 없습니다. 프로세스가 파일을 읽는 데 사용하는 파일 디스크립터에 연결된 위치에 따라 다릅니다. 프로세스가 열리면 다른 프로세스와 동일한 파일을 얻지 만 파일 위치는 독립적입니다./proc/PID/fd/0/proc/PID/fd/0

파이프 인 경우 파이프에 쓰면 파이프 버퍼에 데이터가 추가됩니다. 이 경우 파이프에서 읽는 프로세스가 데이터를 읽습니다./proc/PID/fd/0

터미널 인 경우 터미널에 쓰면 데이터가 터미널에 출력 됩니다. 터미널 파일은 양방향입니다. 파일에 쓰면 데이터가 출력됩니다. 즉 터미널은 텍스트를 표시합니다. 단말기로부터 판독하는 것은 데이터를 입력하는데, 즉 단말기는 사용자 입력을 전송한다./proc/PID/fd/0

파이썬은 터미널에서 읽고 씁니다. 를 실행 하면 터미널에 echo 'print "Hello"' > /proc/$(pidof python)/fd/0쓰고 print "Hello"있습니다. print "Hello"지시에 따라 터미널이 표시 됩니다. 파이썬 프로세스는 아무것도 보지 못하고 여전히 입력을 기다리고 있습니다.

파이썬 프로세스에 입력을 제공하려면 터미널에서 입력을 수행해야합니다. 그렇게하는 방법 은 crasic의 답변 을 참조하십시오 .


2

Gilles가 말한 것을 바탕 으로 터미널에 연결된 프로세스의 표준 입력에 쓰려면 실제로 터미널에 정보를 보내야합니다. 그러나 터미널은 출력뿐만 아니라 입력 형식의 역할도하기 때문에 터미널에 쓸 때 터미널은 "화면"이 아닌 터미널 내에서 실행중인 프로세스에 쓰려고한다는 것을 알 수 없습니다.

그러나 리눅스는 TIOCSTI(터미널 I / O 제어-터미널 입력 시뮬레이션) 이라 불리는 ioctl 요청을 통해 사용자 입력을 시뮬레이션하는 비-포식 방법을 가지고있어 마치 사용자가 입력 한 것처럼 터미널에 문자를 보낼 수 있습니다.

나는 이것이 어떻게 작동하는지 피상적으로 만 알고 있지만 답변을 바탕 으로

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에서 시작되지 않았 음을 관찰하십시오. 이 답변이 작성되기 거의 2 년 전에 사람들은 보안상의 이유로 TIOCSTI를 삭제하기 시작했습니다. unix.stackexchange.com/q/406690/5132
JdeBP

@JdeBP 따라서 "Linux"를 지정합니다 (어디에서 시작했는지 확실하지는 않지만). 그리고 "사람들"은 당신이 BSD를 의미하는 것 같습니까? 필자가이 글을 썼을 때 읽은 내용에 따르면, 훨씬 오래된 구현에서는 패치 된 보안 위험이 있었지만 BSD는 여전히 ioctl을 완전히 삭제하는 것이 안전하다고 생각했습니다. 그러나 나는 이것에 매우 익숙하지 않으므로 해당 시스템에 대한 경험이 없으면 특정 시스템에서 무언가가 불가능하다고 말하지 않는 것이 좋습니다.
Christian Reall-Fluharty
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.