sort pipes exercises linux unix pipe

linux - pipes - ¿Cómo puedo canalizar la entrada inicial al proceso que luego será interactiva?



pipes linux c (4)

Me gustaría poder insertar un comando inicial en el lanzamiento de un proceso interactivo, para que pueda hacer algo como esto:

echo "initial command" | INSERT_MAGIC_HERE some_tool tool> initial command [result of initial command] tool> [now I type an interactive command]

Lo que no funciona:

  • Solo conectar el comando inicial no funciona, ya que esto no se conecta a la terminal.

  • Escribir en / dev / pts / [number] envía la salida al terminal, no ingresa al proceso como si fuera del terminal

Lo que haría, pero con desventajas:

  • Haga un comando que bifurca a un niño, escribe en su stdin y luego reenvía todo desde su propio stdin. Desventaja: las cosas de control de la terminal (como el modo de línea frente a carácter) no funcionarán. ¿Tal vez podría hacer algo con el uso de pseudo terminales?

  • Cree una versión modificada de xterm (de todas formas, voy a ejecutar una para esta tarea) con una opción de línea de comando para inyectar comandos adicionales después de encontrar una cadena de solicitud deseada. Feo.

  • Haga una versión modificada de la herramienta que estoy tratando de ejecutar para que acepte un comando inicial en la línea de comando. Rompe la instalación estándar.

(La herramienta de interés actual, por cierto, es el shell adb de Android: quiero abrir un shell interactivo en el teléfono, ejecutar un comando automáticamente y luego tener una sesión interactiva)


Esto es fácil de hacer con el programa "esperar" que tiene la intención de permitirle escribir scripts para interactuar con los programas.

Probé esto escribiendo un script de espera bc.exp para iniciar la calculadora "bc" y le envié el comando "obase = 16" para ponerlo en modo de salida hexadecimal, y luego darme el control.

La secuencia de comandos (en un archivo llamado bc.exp) es

spawn bc send "obase=16/n" interact { /003 exit }

Uno lo ejecuta con

expect bc.exp


La respuesta aceptada es simple y generalmente buena.

Pero tiene una desventaja: los programas obtienen una tubería como entrada, no una terminal. Esto significa que el autocompletado no funcionará. En muchos casos, esto también desactiva la salida bonita, y he escuchado que algunos programas simplemente se niegan a trabajar si stdin no es una terminal.

El siguiente programa resuelve el problema. Crea un pseudoterminal, genera un programa conectado a este pseudoterminal. Primero alimenta la entrada extra pasada a través de la línea de comandos, y luego lo alimenta a la entrada dada por el usuario a través de stdin.

Por ejemplo, ptypipe "import this" python3 hace que Python ejecute "import this" primero, y luego lo transfiere al prompt interactivo, con finalización de trabajo y otras cosas.

Del mismo modo, ptypipe "date" bash ejecuta Bash, que ejecuta la date y luego le da un shell. Una vez más, con la finalización de trabajo, el prompt colorizado, etc.

#!/usr/bin/env python3 import sys import os import pty import tty import select import subprocess STDIN_FILENO = 0 STDOUT_FILENO = 1 STDERR_FILENO = 2 def _writen(fd, data): while data: n = os.write(fd, data) data = data[n:] def main_loop(master_fd, extra_input): fds = [master_fd, STDIN_FILENO] _writen(master_fd, extra_input) while True: rfds, _, _ = select.select(fds, [], []) if master_fd in rfds: data = os.read(master_fd, 1024) if not data: fds.remove(master_fd) else: os.write(STDOUT_FILENO, data) if STDIN_FILENO in rfds: data = os.read(STDIN_FILENO, 1024) if not data: fds.remove(STDIN_FILENO) else: _writen(master_fd, data) def main(): extra_input = sys.argv[1] interactive_command = sys.argv[2] if hasattr(os, "fsencode"): # convert them back to bytes # http://bugs.python.org/issue8776 interactive_command = os.fsencode(interactive_command) extra_input = os.fsencode(extra_input) # add implicit newline if extra_input and extra_input[-1] != b''/n'': extra_input += b''/n'' # replace LF with CR (shells like CR for some reason) extra_input = extra_input.replace(b''/n'', b''/r'') pid, master_fd = pty.fork() if pid == 0: os.execlp("sh", "/bin/sh", "-c", interactive_command) try: mode = tty.tcgetattr(STDIN_FILENO) tty.setraw(STDIN_FILENO) restore = True except tty.error: # This is the same as termios.error restore = False try: main_loop(master_fd, extra_input) except OSError: if restore: tty.tcsetattr(0, tty.TCSAFLUSH, mode) os.close(master_fd) return os.waitpid(pid, 0)[1] if __name__ == "__main__": main()

(Nota: me temo que esta solución contiene un posible punto muerto. Es posible que desee enviar extra_input en trozos pequeños para evitarlo)


No necesita escribir una nueva herramienta para reenviar stdin ; ya se ha escrito una ( cat ):

(echo "initial command" && cat) | some_tool

Esto tiene la desventaja de conectar una tubería a some_tool , no a una terminal.


Tal vez podrías usar un documento aquí para pasar tu entrada a abd . P.ej. así (usando bc para hacer un cálculo simple como ejemplo).

[axe@gromp ~]$ bc <<END > 3 + 4 > END 7

La sesión bc permanece abierta después, de modo que lo que se proporciona entre los marcadores de inicio y fin (entre "<< END" y "END") se pasará al comando.