Construire une commande en mettant une chaîne dans un tty

15

J'ai réussi à faire ça

echo -n " commande "> / dev / tty1

Les lettres apparaissent et le curseur se déplace, mais ce sont des "fantômes" - si vous frappez Enter, rien ne se passe (ils ne sont pas en stdin).

Éditer:

Au milieu de la capture d'écran ci-dessous, vous voyez pourquoi je vois l'utilisation de cela. (La ligne avec une légende rouge, juste en dessous de la ligne avec une légende jaune.) Comme c'est le cas actuellement, vous n'éditez pas vraiment le texte de la note; il vous est simplement demandé d'écrire un nouveau texte, qui remplacera le texte de la note que vous éditez (pas vraiment). Ainsi, j'ai pensé qu'il pourrait être remédié en collant simplement l'ancien texte dans le tty: si l'utilisateur appuie sur Entrée, aucune modification n'est apportée. (Ce programme est en Perl / MySQL, mais j'ai pensé qu'il serait plus intéressant de demander une solution générale que "comment faire cela en Perl".)

exemple

Modifier 2:

Voici le code Perl, qui utilise le code C ci-dessous (fonctionne exactement comme prévu), ainsi qu'une nouvelle capture d'écran - j'espère que cela clarifiera les choses hors de tout doute :) Encore une fois, regardez au milieu de la capture d'écran, où l'édition est effectuée au texte de la note - cette fois-ci, l'ancien texte est là, par exemple si vous vouliez juste corriger une faute de frappe, vous n'aurez pas à retaper l'intégralité du texte de la note.

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

better_example

Emanuel Berg
la source
Je l'ai fait en Python sur Stack Overflow si cela vous intéresse. stackoverflow.com/a/29616465/117471
Bruno Bronosky
Votre énoncé de problème n'est pas clair. Quel est le problème?

Réponses:

3

Je viens de trouver un petit programme C appelé writevtqui fait l'affaire. Récupérez le code source ici . Pour le compiler, gccsupprimez d'abord les lignes suivantes:

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

Mettre à jour . La commande fait maintenant partie de console-tools , donc disponible dans les systèmes plus récents, à moins que votre distribution utilise kbd au lieu de console-tools , auquel cas vous pouvez la compiler depuis la source (version beaucoup plus récente, aucune modification nécessaire).

Usage:

sudo writevt /dev/ttyN command 

Notez que, pour une raison quelconque, vous devez utiliser '\r'(ou '\x0D') au lieu de '\n'(ou '\x0A') pour envoyer un retour.

Golfe Persique
la source
Cela fonctionne, mais il y a bien plus de mal que ces inclus. J'ai dû abandonner la fonction d'utilisation, faire un prognameet _et commenter quelques appels de fonctionmain()
Michael Mrozek
@MichaelMrozek La _()fonction est généralement un signe d'utilisation de gettext . Semble un peu exagéré pour un morceau de code de démonstration aussi simple mais ne fait pas de mal, je suppose.
jw013
Le lien dans la réponse ci-dessus est rompu. J'en ai trouvé un autre writevt.c ici (sur github.com/  grawity ) ; il semble qu'il s'agisse essentiellement du même programme.
G-Man dit `` Réintègre Monica ''
Ne fonctionne pas pour moi - imprime uniquement la commande. Dending \ r or \ n prinst rn resperctively for any reasson; /
Antoniossss
10

Un terminal se double de deux éléments: un périphérique d'entrée (tel qu'un clavier) et un périphérique d'affichage (tel qu'un moniteur). Lorsque vous lisez à partir du terminal, vous obtenez ce qui vient du périphérique d'entrée. Lorsque vous écrivez sur le terminal, les données vont sur le périphérique d'affichage.

Il n'existe aucun moyen général de forcer l'entrée dans un terminal. Il est rarement nécessaire de le faire. Si vous devez interagir avec un programme qui nécessite un terminal, utilisez un émulateur de terminal dédié tel que Expect ou Empty , ou un wrapper de terminal programmable tel que Screen ou Tmux . Vous pouvez forcer l'entrée dans une console Linux avec un ioctl . Vous pouvez forcer l'entrée dans un émulateur de terminal X11 avec des outils tels que xdotool ou xmacro .

Gilles 'SO- arrête d'être méchant'
la source
J'ai apporté une modification à mon message. Jetez un oeil et vous verrez ma pensée.
Emanuel Berg
@EmanuelBerg Votre modification est difficile à comprendre. Essayez-vous d'introduire par programmation des entrées dans un programme que vous utilisez également de manière interactive? Si c'est ce que vous voulez, exécutez le programme dans screenou tmuxet utilisez leur commande stuff(screen) ou send-key(tmux) ou leur fonction de tampon de collage.
Gilles 'SO- arrête d'être méchant'
A fait une deuxième édition avec le code Perl inclus - l'invocation du binaire C est là. Je ne sais pas ... car c'était si simple (une seule ligne de code) - est-il vraiment préférable de le faire à votre façon (avec les outils screenou tmux)?
Emanuel Berg
@EmanuelBerg Alors oui, vous cherchez screen -X stuff 'note version one'.
Gilles 'SO- arrête d'être méchant'
7

Au moins Linux et BSD ont l'ioctl TIOCSTI pour repousser les caractères vers le tampon d'entrée du terminal (jusqu'à une limite [4096 caractères sous 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);
}

Compilez-le et appelez-le comme:

cmd foo bar < "$some_tty"

pour repousser les caractères sur certains tty.

Et en perl:

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

Edit : je me rends compte maintenant que c'est le même ioctl que dans la solution writevt . Le commentaire et le nom de la commande sont trompeurs car TIOCSTI fonctionne pour n'importe quel terminal, pas seulement pour les VT.

Stéphane Chazelas
la source
Découvrez ma deuxième modification de la question. J'ai déjà compilé le code que j'ai obtenu de @htor - ce que je peux voir, cela fonctionne très bien. Pouvez-vous voir des avantages à utiliser ce code à la place? (Mais merci pour vos efforts dans les deux cas.)
Emanuel Berg
Oui. Voir ma récente modification. Il s'agit d'utiliser l'ioctl TIOCSTI. Le code que j'ai donné ne fait que cela sur le descripteur de fichier 0 (stdin).
Stéphane Chazelas
3

J'ai une démo plus complète sur Stack Overflow .

En python, vous pouvez faire:

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)

Cela suppose une "command"valeur simple ls -laet en utilisant le chemin tty spécifié par OP.

Bruno Bronosky
la source