Scrivere a stdin di un processo


10

Per quanto ho capito se digito quanto segue ...

 python -i

... l'interprete di Python ora leggerà da stdin, comportandosi (ovviamente) in questo modo:

 >>> print "Hello"
 Hello

Mi aspetto che faccia la stessa cosa se lo faccio:

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

Ma questo è l'output (essendo una riga vuota effettiva):

 >>> print "Hello"
 <empyline>

A me sembra che ci sia voluto print "Hello"\ne scritto stdout, senza interpretarlo. Perché non funziona e cosa dovrei fare per farlo funzionare?


Lo ioctl TIOCSTI può scrivere sullo stdin di un terminale come se i dati fossero stati immessi dalla tastiera. Ad esempio github.com/thrig/scripts/blob/master/tty/ttywrite.c
roaima,

Risposte:


9

Inviare input a shell / interpreti in questo modo è molto soggetto a problemi e molto difficile far funzionare in modo affidabile.

Il modo corretto è usare i socket, ecco perché sono stati inventati, puoi farlo nella riga di comando usando ncat nco socatper associare un processo Python a un semplice socket. Oppure scrivi una semplice applicazione Python che si lega alla porta e ascolta i comandi da interpretare su un socket.

le prese possono essere locali e non esposte a nessuna interfaccia web.


Il problema è che se si avvia pythondalla riga di comando, in genere è collegato alla shell che è collegata a un terminale, infatti possiamo vedere

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

quindi quando scrivi a stdindi Python, stai effettivamente scrivendo sul ptypsuedo-terminale, che è un dispositivo del kernel, non un semplice file. Esso utilizza ioctlNon reade write, così potrete vedere l'uscita sullo schermo, ma non sarà inviato al processo generato ( python)

Un modo per replicare quello che stai provando è con un fifoo 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

È inoltre possibile utilizzare screensolo per l'input

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

Se si tiene aperto il tubo (ad es. sleep 300 > python_i.pipe &) L'altro lato non si chiuderà e pythoncontinuerà ad accettare comandi lungo il tubo. Non esiste un EOF in quanto tale inviato da echo.
roaima,

@roaima hai ragione, ho sbagliato a capire che l'eco invia l'EOF quando chiude il flusso. Questo non è evitabile con| tubi, tuttavia, è corretto?
crasic,

Ero già in fondo alla Fifo Road, ma echo something > fifoavrei causato un EOF che avrebbe bloccato molte applicazioni. La sleep infinity > fifosoluzione non ha attraversato la mia metà però, grazie!
Sheppy,

1
continuando davvero la tua idea, puoi anche fare ciò python -i <> fifoche impedirà anche l'EOF
Sheppy

10

L' accesso non accede al descrittore di file 0 del processo PID , accede al file che PID ha aperto sul descrittore di file 0. Questa è una distinzione sottile, ma è importante. Un descrittore di file è una connessione che un processo ha a un file. La scrittura su un descrittore di file scrive sul file indipendentemente da come il file è stato aperto./proc/PID/fd/0

Se è un file normale, scrivendolo si modifica il file. I dati non sono necessariamente ciò che il processo leggerà in seguito: dipende dalla posizione allegata al descrittore di file che il processo sta utilizzando per leggere il file. Quando si apre un processo , ottiene lo stesso file dell'altro processo, ma le posizioni dei file sono indipendenti./proc/PID/fd/0/proc/PID/fd/0

Se è una pipe, la scrittura su di essa aggiunge i dati al buffer della pipe. In tal caso, il processo che legge dalla pipe leggerà i dati./proc/PID/fd/0

Se è un terminale, la scrittura su di esso genera i dati su un terminale. Un file terminale è bidirezionale: scrivendo su di esso vengono emessi i dati, ovvero il terminale visualizza il testo; la lettura da un terminale immette i dati, ovvero il terminale trasmette l'input dell'utente./proc/PID/fd/0

Python legge e scrive sul terminale. Quando corri echo 'print "Hello"' > /proc/$(pidof python)/fd/0, stai scrivendo print "Hello"sul terminale. Il terminale viene visualizzato print "Hello"come indicato. Il processo Python non vede nulla, sta ancora aspettando l'input.

Se si desidera alimentare l'input al processo Python, è necessario ottenere il terminale per farlo. Vedere la risposta di crasic per i modi per farlo.


2

Basandosi su ciò che ha detto Gilles , se vogliamo scrivere sullo stdin di un processo collegato a un terminale, dobbiamo effettivamente inviare le informazioni al terminale. Tuttavia, poiché un terminale funge sia da forma di input che di output, quando si scrive su di esso il terminale non ha modo di sapere che si desidera scrivere su un processo in esecuzione al suo interno piuttosto che sullo "schermo".

Linux ha comunque un modo non posix di simulare l'input dell'utente attraverso una richiesta ioctl chiamata TIOCSTI(Terminal I / O Control - Simulate Terminal Input) che ci consente di inviare caratteri a un terminale come se fossero stati digitati da un utente.

Sono solo superficialmente consapevole di come funziona, ma sulla base di questa risposta, dovrebbe essere possibile farlo con qualcosa sulla falsariga di

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

Alcune risorse esterne:

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

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


Si noti che la domanda non è specifica per alcun sistema operativo particolare e che TIOCSTI non ha avuto origine con Linux. Quasi due anni prima che questa risposta fosse scritta, le persone hanno iniziato a lasciare TIOCSTI per motivi di sicurezza. unix.stackexchange.com/q/406690/5132
JdeBP

@JdeBP Da qui il mio specificare "Linux" (anche se non sono sicuro di dove sia nato). E per "persone", sembra che intendi alcuni BSD? Da quello che ho letto quando ho scritto questo, sembra che ci fosse, in un'implementazione molto più antica, un rischio per la sicurezza che da allora è stato patchato, ma che BSD ha ancora ritenuto "più sicuro" eliminare del tutto lo ioctl. Tuttavia non ho molta familiarità con nulla di tutto ciò, quindi ho pensato che fosse meglio non dire che qualcosa non era possibile su alcuni sistemi quando non ho esperienza con quel sistema.
Christian Reall-Fluharty,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.