เขียนถึง stdin ของกระบวนการ


10

เท่าที่ฉันเข้าใจถ้าฉันพิมพ์ต่อไปนี้ ...

 python -i

... python-interpreter จะอ่านจาก stdin, ประพฤติ (ชัด) เช่นนี้:

 >>> print "Hello"
 Hello

ฉันคาดหวังว่ามันจะทำสิ่งเดียวกันถ้าฉันทำสิ่งนี้:

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

แต่นี่คือผลลัพธ์ (beeing บรรทัดว่างจริง):

 >>> print "Hello"
 <empyline>

สิ่งนี้สำหรับฉันดูเหมือนว่าเพิ่งใช้print "Hello"\nและเขียนถึง stdoutแต่ไม่ได้ตีความ ทำไมมันไม่ทำงานและฉันต้องทำอย่างไรเพื่อให้มันทำงาน?


TIOCSTI ioctl สามารถเขียนไปยังstdinของเทอร์มินัลราวกับว่าข้อมูลถูกป้อนจากคีย์บอร์ด ตัวอย่างเช่นgithub.com/thrig/scripts/blob/master/tty/ttywrite.c
roaima

คำตอบ:


9

การส่งอินพุตไปยัง shells / interpreters ด้วยวิธีนี้จะทำให้เกิดปัญหาและยากที่จะทำงานในลักษณะที่เชื่อถือได้

วิธีที่เหมาะสมคือการใช้ซ็อกเก็ตนี่คือเหตุผลที่พวกเขาถูกประดิษฐ์คุณสามารถทำได้ในบรรทัดคำสั่งโดยใช้ncat ncหรือsocatเพื่อผูกกระบวนการหลามกับซ็อกเก็ตที่เรียบง่าย หรือเขียนแอพพลิเคชั่นหลามแบบง่าย ๆ ที่ผูกเข้ากับพอร์ตและฟังคำสั่งเพื่อตีความบนซ็อกเก็ต

ซ็อกเก็ตสามารถอยู่ในพื้นที่และไม่ได้สัมผัสกับเว็บอินเตอร์เฟสใด ๆ


ปัญหาคือว่าถ้าคุณเริ่มต้นpythonจากบรรทัดคำสั่งมันมักจะแนบมากับเปลือกของคุณซึ่งติดอยู่กับสถานีในความเป็นจริงเราสามารถเห็น

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

ดังนั้นเมื่อคุณเขียนถึงstdinของ python คุณกำลังเขียนไปยังptypsuedo-terminal ซึ่งเป็นอุปกรณ์เคอร์เนลไม่ใช่ไฟล์ง่ายๆ มันioctlไม่ได้ใช้readและwriteดังนั้นคุณจะเห็นผลลัพธ์บนหน้าจอของคุณ แต่มันจะไม่ถูกส่งไปยังกระบวนการเกิด ( python)

วิธีหนึ่งที่จะทำซ้ำสิ่งที่คุณกำลังพยายามอยู่กับหรือfifonamed 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 คุณพูดถูกฉันเข้าใจผิดว่า echo ส่ง EOF เมื่อปิดสตรีม จะไม่สามารถหลีกเลี่ยงได้กับ|ท่ออย่างไรถูกต้อง?
crasic

ฉันไปตามถนน Fifo แล้ว แต่echo something > fifoจะทำให้ได้ EOF ซึ่งจะหยุดการใช้งานหลายอย่าง sleep infinity > fifoวิธีแก้ปัญหาไม่ได้ข้ามกลางของฉันแม้ว่าขอขอบคุณ!
Sheppy

1
คุณยังสามารถทำเช่นนั้นได้python -i <> fifoซึ่งจะป้องกัน EOF
Sheppy

10

การเข้าถึงไม่สามารถเข้าถึง file descriptor 0 ของกระบวนการPID ได้แต่จะเข้าถึงไฟล์ที่PIDได้เปิดไว้บน file descriptor 0 นี่คือความแตกต่างที่ลึกซึ้ง แต่มันสำคัญ file descriptor เป็นการเชื่อมต่อที่กระบวนการมีต่อไฟล์ การเขียนไฟล์ descriptor เขียนไปยังไฟล์โดยไม่คำนึงถึงวิธีการเปิดไฟล์/proc/PID/fd/0

หากเป็นไฟล์ปกติการเขียนลงไฟล์จะเป็นการแก้ไข ข้อมูลไม่จำเป็นต้องเป็นสิ่งที่กระบวนการจะอ่านต่อไป: มันขึ้นอยู่กับตำแหน่งที่แนบมากับ file descriptor ที่กระบวนการใช้ในการอ่านไฟล์ เมื่อกระบวนการเปิดขึ้นมาไฟล์นั้นจะได้รับไฟล์เดียวกันกับกระบวนการอื่น แต่ตำแหน่งไฟล์นั้นจะเป็นอิสระ/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 คุณจะต้องรับเครื่องปลายทางเพื่อดำเนินการ ดูคำตอบของ crasicสำหรับวิธีการทำเช่นนั้น


2

การสร้างสิ่งที่Gilles พูดถ้าเราต้องการเขียนไปยัง stdin ของกระบวนการที่ติดกับเทอร์มินัลเราจำเป็นต้องส่งข้อมูลไปยังเทอร์มินัล อย่างไรก็ตามเนื่องจากเทอร์มินัลทำหน้าที่เป็นรูปแบบของอินพุตเช่นเดียวกับเอาท์พุตเมื่อทำการเขียนลงไปเทอร์มินัลจะไม่มีทางรู้ว่าคุณต้องการที่จะเขียนไปยังกระบวนการที่ทำงานอยู่ภายในแทนที่จะเป็น "หน้าจอ"

อย่างไรก็ตามลีนุกซ์มีวิธีการจำลองการป้อนข้อมูลผู้ใช้ผ่านทางคำขอ ioctl ที่เรียกว่าTIOCSTI(การควบคุมเทอร์มินัล I / O - การจำลองเทอร์มินัลอินพุต) ซึ่งช่วยให้เราสามารถส่งอักขระไปยังเทอร์มินัลราวกับว่าผู้ใช้พิมพ์

ฉันเพียงแค่เผินๆรู้ดีว่ามันทำงานอย่างไร แต่จากคำตอบนี้มันเป็นไปได้ที่จะทำสิ่งนี้กับบางสิ่ง

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 ยังคงคิดว่า "ปลอดภัย" เพื่อทิ้ง ioctl โดยสิ้นเชิง อย่างไรก็ตามฉันไม่คุ้นเคยกับสิ่งนี้มากนักดังนั้นฉันจึงคิดว่าไม่ควรพูดว่ามีบางสิ่งที่เป็นไปไม่ได้ในบางระบบเมื่อฉันไม่มีประสบการณ์กับระบบนั้น
Christian Reall-Fluharty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.