Erstellen Sie einen Befehl, indem Sie eine Zeichenfolge in ein tty einfügen

15

Das habe ich geschafft

echo -n " Befehl "> / dev / tty1

Die Buchstaben erscheinen und der Cursor bewegt sich, aber sie sind "Geister" - wenn Sie drücken Enter, passiert nichts (sie sind nicht im Standard).

Bearbeiten:

In der Mitte des folgenden Screenshots sehen Sie, warum ich die Verwendung dieses sehe. (Die Zeile mit der roten Beschriftung, direkt unter der Zeile mit der gelben Beschriftung.) Sie bearbeiten den Hinweistext derzeit nicht wirklich. Sie werden lediglich aufgefordert, einen neuen Text zu schreiben, der den Text der Notiz ersetzt, die Sie (nicht wirklich) bearbeiten. Daher dachte ich, es könnte durch einfaches Einfügen des alten Textes in das tty behoben werden: Wenn der Benutzer die Eingabetaste drückt, wird keine Änderung vorgenommen. (Dieses Programm ist in Perl / MySQL, aber ich dachte, es wäre interessanter, nach einer allgemeinen Lösung zu fragen, als "wie mache ich das in Perl".)

Beispiel

Bearbeiten 2:

Hier ist der Perl-Code, der den C-Code unten verwendet (funktioniert genau wie vorgesehen), sowie ein neuer Screenshot - dies wird hoffentlich die Dinge zweifelsfrei klären :) Schauen Sie sich noch einmal die Mitte des Screenshots an, in der die Bearbeitung erfolgt zum Notizentext - diesmal ist der alte Text vorhanden. Wenn Sie beispielsweise nur einen Tippfehler korrigieren möchten, müssen Sie nicht den gesamten Notizentext erneut eingeben.

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
quelle
Ich habe dies in Python über Stack Overflow gemacht, wenn Sie interessiert sind. stackoverflow.com/a/29616465/117471
Bruno Bronosky
Ihre Problemstellung ist nicht klar. Worin besteht das Problem?

Antworten:

3

Ich habe gerade ein kleines C-Programm namens gefunden writevt, das den Trick macht. Besorgen Sie sich den Quellcode hier . Um es zu kompilieren, gccentfernen Sie zuerst die folgenden Zeilen:

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

Update . Der Befehl ist jetzt Teil der Konsolentools und steht daher in neueren Systemen zur Verfügung, sofern Ihre Distribution nicht kbd anstelle der Konsolentools verwendet . In diesem Fall können Sie ihn aus dem Quellcode kompilieren (viel neuere Version, keine Änderung erforderlich).

Verwendung:

sudo writevt /dev/ttyN command 

Beachten Sie, dass Sie aus irgendeinem Grund '\r'(oder '\x0D') anstelle von '\n'(oder '\x0A') verwenden müssen, um eine Rückgabe zu senden.

Persischer Golf
quelle
Das funktioniert, aber es gibt viel mehr als nur die Einschlüsse. Ich musste die Verwendungsfunktion aufgeben, ein prognameund _auskommentieren und ein paar Funktionsaufrufe einfügenmain()
Michael Mrozek
@MichaelMrozek Die _()Funktion ist normalerweise ein Zeichen dafür, dass gettext verwendet wird. Scheint ein bisschen übertrieben für ein so einfaches Stück Demo-Code, schadet aber wohl nicht.
JW013
Der Link in der obigen Antwort ist unterbrochen. Ich fand eine andere writevt.c hier (bei github.com/  grawity ) ; es scheint im Wesentlichen das gleiche Programm zu sein.
G-Man sagt, dass Monica
Funktioniert bei mir nicht - druckt nur den Befehl. Dending \ r oder \ n prinst rn respektive aus irgendeinem Grund; /
Antoniossss
10

Ein Terminal hat zwei Funktionen: ein Eingabegerät (z. B. eine Tastatur) und ein Anzeigegerät (z. B. ein Monitor). Wenn Sie vom Terminal lesen, erhalten Sie, was vom Eingabegerät kommt. Wenn Sie in das Terminal schreiben, werden die Daten auf das Anzeigegerät übertragen.

Es gibt keine allgemeine Möglichkeit, Eingaben in ein Terminal zu erzwingen. Es gibt selten eine Notwendigkeit, dies zu tun. Wenn Sie mit einem Programm interagieren müssen, für das ein Terminal erforderlich ist, verwenden Sie einen dedizierten Terminal-Emulator wie Expect oder Empty oder einen programmierbaren Terminal-Wrapper wie Screen oder Tmux . Sie können die Eingabe in eine Linux-Konsole mit einem ioctl erzwingen . Sie können die Eingabe in einen X11-Terminalemulator mit Tools wie xdotool oder xmacro erzwingen .

Gilles 'SO - hör auf böse zu sein'
quelle
Ich habe meinen Beitrag bearbeitet. Schauen Sie sich das an und Sie werden sehen, wie ich nachdenke.
Emanuel Berg
@EmanuelBerg Deine Bearbeitung ist schwer zu verstehen. Versuchen Sie, Eingaben programmgesteuert in ein Programm einzuspeisen, das Sie auch interaktiv verwenden? Wenn Sie dies wünschen, führen Sie das Programm in screenoder aus tmuxund verwenden Sie den Befehl stuff(screen) oder send-key(tmux) oder die Funktion zum Einfügen von Puffern.
Gilles 'SO- hör auf böse zu sein'
Durchgeführt eine zweite Bearbeitung mit dem Perl-Code enthalten - der Aufruf der C-Binärdatei ist da. Ich weiß es nicht ... da es so einfach war (nur eine Codezeile) - ist es wirklich besser, es auf Ihre Art zu machen (mit dem screenoder den tmuxWerkzeugen)?
Emanuel Berg
@EmanuelBerg Also ja, du suchst screen -X stuff 'note version one'.
Gilles 'SO- hör auf böse zu sein'
7

Mindestens Linux und BSDs verfügen über das TIOCSTI ioctl, um Zeichen in den Terminal-Eingabepuffer zurückzuschieben (bis zu einer Grenze von [4096 Zeichen unter 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);
}

Kompilieren Sie es und nennen Sie es wie folgt:

cmd foo bar < "$some_tty"

Zeichen auf einige tty zurückschieben.

Und in Perl:

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

Edit : Mir ist jetzt klar, dass es das gleiche ioctl ist wie in der Writevt- Lösung. Der Kommentar und der Name des Befehls sind irreführend, da TIOCSTI für jedes Terminal funktioniert, nicht nur für VTs.

Stéphane Chazelas
quelle
Schauen Sie sich meine zweite Bearbeitung der Frage an. Ich habe den Code, den ich von @htor bekommen habe, bereits kompiliert - was ich sehe, funktioniert es großartig. Können Sie irgendwelche Vorteile sehen, wenn Sie stattdessen diesen Code verwenden? (Aber danke für Ihre Bemühungen in beiden Fällen.)
Emanuel Berg
Ja. Siehe meine letzte Bearbeitung. Es geht darum, den TIOCSTI ioctl zu verwenden. Der Code, den ich gegeben habe, tut genau das auf Dateideskriptor 0 (stdin).
Stéphane Chazelas
2

Ich habe eine vollständigere Demo über Stack Overflow .

In Python können Sie:

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)

Dies setzt einen einfachen "command"Wert für ls -laund die Verwendung des von OP angegebenen tty-Pfads voraus.

Bruno Bronosky
quelle