Skonstruuj polecenie, wstawiając ciąg do tty

15

Udało mi się to zrobić

echo -n " polecenie "> / dev / tty1

Pojawiają się litery i kursor się porusza, ale są one „duchami” - jeśli trafisz Enter, nic się nie wydarzy (nie są ustawione na standardowe).

Edytować:

Pośrodku poniższego zrzutu ekranu widać, dlaczego widzę zastosowanie tego. (Linia z czerwonym podpisem, tuż pod linią z żółtym podpisem.) W tej chwili tak naprawdę nie „edytujesz” tekstu notatki; zostałeś poproszony o napisanie nowego tekstu, który zastąpi tekst notatki, którą (tak naprawdę) nie edytujesz. Pomyślałem więc, że można temu zaradzić, po prostu wklejając stary tekst do tty: jeśli użytkownik naciśnie klawisz Enter, nie zostaną wprowadzone żadne zmiany. (Ten program jest w Perlu / MySQL, ale pomyślałem, że bardziej interesujące byłoby poprosić o ogólne rozwiązanie niż „jak to zrobić w Perlu”).

przykład

Edycja 2:

Oto kod Perla, który używa poniższego kodu C (działa dokładnie zgodnie z przeznaczeniem), a także nowy zrzut ekranu - mam nadzieję, że wyjaśni to ponad wszelką wątpliwość :) Ponownie spójrz na środek zrzutu ekranu, w którym dokonano edycji do tekstu notatki - tym razem znajduje się stary tekst, na przykład jeśli chcesz tylko poprawić literówkę, nie będziesz musiał ponownie wpisywać całego tekstu notatki.

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

lepszy_przykład

Emanuel Berg
źródło
Zrobiłem to w Pythonie na Stack Overflow, jeśli jesteś zainteresowany. stackoverflow.com/a/29616465/117471
Bruno Bronosky
Twoje zgłoszenie problemu nie jest jasne. Jaki jest problem?

Odpowiedzi:

3

Właśnie znalazłem mały program o nazwie C, writevtktóry działa. Pobierz kod źródłowy tutaj . Aby go skompilować, gccnajpierw usuń następujące linie:

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

Aktualizacja . Polecenie jest teraz częścią narzędzi konsolowych , a zatem dostępnych w nowszych systemach, chyba że twoja dystrybucja używa kbd zamiast narzędzi konsolowych , w którym to przypadku możesz skompilować go ze źródła (znacznie nowsza wersja, nie jest wymagana modyfikacja).

Stosowanie:

sudo writevt /dev/ttyN command 

Pamiętaj, że z jakiegoś powodu musisz użyć '\r'(lub '\x0D') zamiast '\n'(lub '\x0A'), aby wysłać zwrot.

Zatoka Perska
źródło
To działa, ale jest znacznie więcej błędów niż tylko te. Musiałem porzucić funkcję użytkowania, zrobić prognamea _i skomentować kilka wywołań funkcjimain()
Michael Mrozek
@MichaelMrozek _()Funkcja jest zwykle oznaką użycia gettext . Wydaje się to trochę przesadne jak na tak prosty fragment kodu demonstracyjnego, ale chyba nie zaszkodzi.
jw013,
Ogniwo w powyższym odpowiedź jest uszkodzony. Znalazłem inny writevt.c tutaj (na github.com/  grawity ) ; wydaje się, że jest to zasadniczo ten sam program.
G-Man mówi „Przywróć Monikę”
Nie działa dla mnie - drukuje tylko polecenie. W oczekiwaniu na odpowiedź lub niezależnie od przyczyny; /
Antoniossss
10

Terminal podwaja się jako dwie rzeczy: urządzenie wejściowe (takie jak klawiatura) i urządzenie wyświetlające (takie jak monitor). Kiedy czytasz z terminala, dostajesz to, co pochodzi z urządzenia wejściowego. Gdy piszesz do terminala, dane trafiają na urządzenie wyświetlające.

Nie ma ogólnego sposobu na zmuszanie wejścia do terminala. Rzadko jest taka potrzeba. Jeśli potrzebujesz wejść w interakcję z programem, który wymaga terminala, użyj dedykowanego emulatora terminala, takiego jak Expect lub Empty , lub programowalnego otoki terminala, takiego jak Screen lub Tmux . Możesz wymusić wejście do konsoli Linux za pomocą ioctl . Możesz wymusić wejście do emulatora terminala X11 za pomocą narzędzi takich jak xdotool lub xmacro .

Gilles „SO- przestań być zły”
źródło
Dokonałem edycji mojego postu. Spójrz, a zobaczysz moje myślenie.
Emanuel Berg,
@EmanuelBerg Twoja zmiana jest trudna do zrozumienia. Czy próbujesz programowo wprowadzić dane wejściowe do programu, którego używasz również interaktywnie? Jeśli tego chcesz, uruchom program w screenlub tmuxi użyj ich polecenia stuff(screen) lub send-key(tmux) lub funkcji bufora wklejania.
Gilles „SO- przestań być zły”
Wykonałem drugą edycję z dołączonym kodem Perla - jest tam wywołanie pliku binarnego C. Nie wiem ... bo to było takie proste (tylko jedna linia kodu) - czy naprawdę lepiej jest robić to po swojemu (za pomocą narzędzi screenlub tmux)?
Emanuel Berg
@EmanuelBerg Więc tak, szukasz screen -X stuff 'note version one'.
Gilles „SO- przestań być zły”
7

Przynajmniej Linux i BSD mają ioctl TIOCSTI do wypychania znaków z powrotem do bufora wejściowego terminala (do limitu [4096 znaków w Linuksie]):

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

Skompiluj go i wywołaj jako:

cmd foo bar < "$some_tty"

aby zepchnąć postacie z powrotem na jakiś tty.

I w perlu:

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

Edycja : Zdaję sobie sprawę, że teraz jest to ten sam ioctl, co w rozwiązaniu zapisu . Komentarz i nazwa polecenia są mylące, ponieważ TIOCSTI działa na dowolnym terminalu, nie tylko VT.

Stéphane Chazelas
źródło
Sprawdź moją drugą edycję pytania. Skompilowałem już kod, który otrzymałem od @htor - co widzę, działa świetnie. Czy widzisz jakieś zalety korzystania z tego kodu? (Ale dziękuję za twój wysiłek w obu przypadkach.)
Emanuel Berg
Tak. Zobacz moją ostatnią edycję. Chodzi o to, aby użyć ioctl TIOCSTI. Kod, który podałem, robi to tylko na deskryptorze pliku 0 (stdin).
Stéphane Chazelas
Nie każdy ma już TIOCSTI. Pięć lat po napisaniu tej odpowiedzi ludzie zaczęli upuszczać ją z jądra. unix.stackexchange.com/q/406690/5132
JdeBP
3

Mam pełniejsze demo na Stack Overflow .

W pythonie możesz wykonać:

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)

To zakłada prostą "command"wartość ls -lai użycie ścieżki tty określonej przez OP.

Bruno Bronosky
źródło