Construya un comando poniendo una cadena en un tty

15

Logré hacer esto

echo -n " comando "> / dev / tty1

Aparecen las letras y el cursor se mueve, pero son "fantasmas". Si golpeas Enter, no pasa nada (no están en stdin).

Editar:

En el medio de la siguiente captura de pantalla, puede ver por qué veo el uso de esto. (La línea con un subtítulo rojo, justo debajo de la línea con un subtítulo amarillo). Tal como está ahora, realmente no está "editando" el texto de la nota; solo se le pide que escriba un nuevo texto, que reemplazará el texto de la nota que está editando (no realmente). Por lo tanto, pensé que podría remediarse simplemente pegando el texto antiguo en el tty: si el usuario presiona enter, no se realiza ninguna modificación. (Este programa está en Perl / MySQL, pero pensé que sería más interesante pedir una solución general que "cómo hago esto en Perl").

ejemplo

Edición 2:

Aquí está el código de Perl, que usa el código C a continuación (funciona exactamente como se esperaba), así como una nueva captura de pantalla; con suerte, esto aclarará las cosas más allá de toda duda :) Nuevamente, mire el centro de la captura de pantalla, donde se realiza la edición al texto de la nota: esta vez, el texto anterior está allí, por ejemplo, si solo desea corregir un error tipográfico, no tendrá que volver a escribir todo el texto de la nota.

my $edit_note_text = $edit_note_data[2];
print BOLD, RED, " new text: ", RESET;
system("writevt /dev/tty \"$edit_note_text\"");
my $new_text = <$in>;
$new_text = fix_input($new_text);
my $set_text = "UPDATE notes SET note = \"$new_text\" WHERE id = $edit_note_id";
$db->do($set_text);

mejor_ejemplo

Emanuel Berg
fuente
Hice esto en Python en Stack Overflow si estás interesado. stackoverflow.com/a/29616465/117471
Bruno Bronosky
Su enunciado del problema no está claro. ¿Cuál es el problema?

Respuestas:

3

Acabo de encontrar un pequeño programa en C llamado writevtque hace el truco. Toma el código fuente aquí . Para que se compile con gccsolo elimine las siguientes líneas primero:

#include <lct/cline.h>
#include <lct/utils.h>

Actualización . El comando ahora forma parte de las herramientas de la consola , por lo tanto, está disponible en los sistemas más recientes, a menos que su distribución use kbd en lugar de las herramientas de la consola , en cuyo caso puede compilarlo desde la fuente (versión mucho más reciente, no necesita modificación).

Uso:

sudo writevt /dev/ttyN command 

Tenga en cuenta que, por alguna razón, debe usar '\r'(o '\x0D') en lugar de '\n'(o '\x0A') para enviar una devolución.

Golfo pérsico
fuente
Esto funciona, pero hay mucho más mal que solo esos incluidos. Tuve que deshacerme de la función de uso, hacer un prognamey _, y comentar algunas llamadas de funciónmain()
Michael Mrozek
@MichaelMrozek La _()función suele ser un signo de que se está utilizando gettext . Parece un poco exagerado para una pieza tan simple de código de demostración, pero no hace daño, supongo.
jw013
El enlace en la respuesta anterior está roto. Encontré otro writevt.c aquí (en  github.com/grawity ) ; parece ser esencialmente el mismo programa.
G-Man dice 'reinstalar a Monica' el
No funciona para mí, solo imprime el comando. Dending \ r o \ n prinst rn de forma respersiva por cualquier razón; /
Antoniossss
10

Un terminal funciona como dos cosas: un dispositivo de entrada (como un teclado) y un dispositivo de visualización (como un monitor). Cuando lees desde el terminal, obtienes lo que proviene del dispositivo de entrada. Cuando escribe en el terminal, los datos van al dispositivo de visualización.

No hay una forma general de forzar la entrada a un terminal. Rara vez hay necesidad de hacerlo. Si necesita interactuar con un programa que requiere un terminal, use un emulador de terminal dedicado como Expect o Empty , o un contenedor de terminal programable como Screen o Tmux . Puede forzar la entrada a una consola Linux con un ioctl . Puede forzar la entrada en un emulador de terminal X11 con herramientas como xdotool o xmacro .

Gilles 'SO- deja de ser malvado'
fuente
Hice una edición en mi publicación. Echa un vistazo y verás mi pensamiento.
Emanuel Berg
@EmanuelBerg Tu edición es difícil de entender. ¿Estás tratando de alimentar de forma programática la entrada a un programa que también estás usando de forma interactiva? Si eso es lo que desea, ejecute el programa en screeno tmuxy use su comando stuff(pantalla) o send-key(tmux) o su función de pegar búfer.
Gilles 'SO- deja de ser malvado'
Hizo una segunda edición con el código Perl incluido: la invocación del binario C está ahí. No sé ... ya que era tan simple (solo una línea de código), ¿es realmente mejor hacerlo a su manera (con las herramientas screeno tmux)?
Emanuel Berg
@EmanuelBerg Entonces sí, lo estás buscando screen -X stuff 'note version one'.
Gilles 'SO- deja de ser malvado'
7

Al menos Linux y BSD tienen el ioctl TIOCSTI para devolver los caracteres al búfer de entrada del terminal (hasta un límite [4096 caracteres en Linux]):

#include <sys/ioctl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>

void stackchar(char c)
{
  if (ioctl(0, TIOCSTI, &c) < 0) {
    perror("ioctl");
    exit(1);
  }
}
int main(int argc, char *argv[])
{
  int i, j;
  char c;

  for (i = 1; i < argc; i++) {
    if (i > 1) stackchar(' ');
    for (j=0; (c = argv[i][j]); j++) {
      stackchar(c);
    }
  }
  exit(0);
}

Compílelo y llámelo como:

cmd foo bar < "$some_tty"

para hacer retroceder a los personajes en algunos tty.

Y en perl:

require "sys/ioctl.ph";
ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV;

Editar : Ahora me doy cuenta de que es el mismo ioctl que en la solución writevt . El comentario y el nombre del comando son engañosos, ya que TIOCSTI funciona para cualquier terminal, no solo para VT.

Stéphane Chazelas
fuente
Mira mi segunda edición de la pregunta. Ya compilé el código que obtuve de @htor; lo que puedo ver, funciona muy bien. ¿Puedes ver alguna ventaja usando este código en su lugar? (Pero gracias por su esfuerzo en cualquier caso.)
Emanuel Berg
Si. Ver mi edición reciente. El punto es usar el TIOCSTI ioctl. El código que di hace exactamente eso en el descriptor de archivo 0 (stdin).
Stéphane Chazelas
3

Tengo una demostración más completa sobre Stack Overflow .

En python puedes hacer:

import fcntl
import sys
import termios

with open('/dev/tty1', 'w') as fd:
    for char in "ls -la\n":
        fcntl.ioctl(fd, termios.TIOCSTI, char)

Eso supone un "command"valor simple ls -lay utiliza la ruta tty especificada por OP.

Bruno Bronosky
fuente