W jaki sposób `less` pobiera dane ze standardowego wejścia, gdy nadal może czytać polecenia od użytkownika?

47

Jak większość z was robiła wiele razy, wygodnie jest przeglądać długi tekst za pomocą less:

some_command | less

Teraz jego standardowe wejście jest podłączone do rury (FIFO). Jak nadal może czytać polecenia takie jak góra / dół / wyjście?

iBug
źródło
15
lessodczytuje dane do wyświetlenia ze standardowego wejścia i odczytuje polecenia z tty. To są różne rzeczy.
William Pursell
2
@WilliamPursell Tak, wiem. Ale jest tylko jeden standardowy strumień wejściowy, prawda?
iBug
4
Tak, jest jeden strumień wejściowy i jeden tty. lessodczytuje dane ze standardowego wejścia i polecenia z tty.
William Pursell

Odpowiedzi:

52

Jak wspomniał William Pursell , lessodczytuje naciśnięcia klawiszy użytkownika z terminala. Otwiera się jawnie /dev/tty, terminal kontrolny; co daje mu deskryptor pliku, niezależny od standardowego wejścia, z którego może odczytać interaktywne dane wejściowe użytkownika. W razie potrzeby może jednocześnie odczytać dane do wyświetlenia ze standardowego wejścia. (W razie potrzeby może również pisać bezpośrednio do terminala).

Możesz to zobaczyć, uruchamiając

some_command | strace -o less.trace -e open,read,write less

Poruszaj się po wejściu, wyjdź lessi spójrz na zawartość less.trace: zobaczysz, że jest otwarta /dev/tty, i czytaj zarówno z deskryptora pliku 0, jak i tego, który zwrócił podczas otwierania /dev/tty(prawdopodobnie 3).

Jest to powszechna praktyka dla programów, które chcą mieć pewność, że czytają i piszą na terminalu. Jednym z przykładów jest SSH, np. Gdy prosi o hasło lub hasło.

Jak wyjaśniono przez Schily , jeśli /dev/ttynie można otworzyć, lessbędzie czytać z jej błędu standardowego (deskryptor pliku 2). lessużycie /dev/ttyzostało wprowadzone w wersji 177, wydanej 2 kwietnia 1991 roku.

Jeśli spróbujesz działa cat /dev/tty | less, jak sugerowano przez Hagen von EITZEN , lessuda się otwarcie /dev/tty, ale nie otrzyma żadnych danych z niego aż catje zamyka. Więc zobaczysz ekran pusty i nic więcej, dopóki nie naciśniesz, CtrlCaby zabić cat(lub zabić w inny sposób); Następnie lesspokaże cokolwiek wpisane natomiast catzostał uruchomiony, i pozwalają go kontrolować.

Stephen Kitt
źródło
4
@HagenvonEitzen Twój komputer eksploduje! To tak, jak Kirk i Spock spowodowali rozbicie androidów Mudda.
Barmar
7
@HagenvonEitzen Wow. Podwójnie bezużyteczne użycie kota . Jestem pod wrażeniem.
Andrew Henle,
8
@grawity Myślę, że Andrew ma na myśli to, że cat blah |można go zastąpić < blah, a nawet to nie jest konieczne w tym przypadku, ponieważ less blahrównież działa (dobrze less -f /dev/tty). Ale czytanie ze /dev/ttyjest trochę szczególny przypadek, a wszystkie trzy warianty ( cat /dev/tty | less, less < /dev/ttyi less -f /dev/tty) dają różne wyniki.
Stephen Kitt
1
Czy / dev / tty zawsze w jakiś sposób wskazuje właściwe miejsce? Myślałem, że zwykle będziesz potrzebować / dev / ptsX?
StarWeaver
2
@StarWeaver zobacz to pytanie o różnicę między /dev/ttyi /dev/pts/....
Stephen Kitt
26

UNIX oferuje dwie metody odczytu danych wejściowych użytkowników, podczas gdy stdin został przekierowany:

  • Oryginalną metodą jest czytanie ze stderr . Stderr jest otwarty na pisanie i czytanie, o czym wciąż wspomina POSIX.

  • Późniejsze wersje systemu UNIX (około 1979 r.) /dev/ttyDodały interfejs sterownika, który pozwala na otwarcie kontrolnego procesu. Ponieważ istnieją procesy bez kontrolnego urządzenia tty, próba otwarcia może się /dev/ttynie powieść. Przyjazne pisane oprogramowanie ma zatem powrót do oryginalnej metody, a następnie próbuje odczytać ze stderr.

schily
źródło
11
Czytałeś ze stderr? Nauczyłem się czegoś nowego.
iBug
1
Cieszę się, że ktoś pamięta stare sposoby.
Joshua
3
Czy powodem jest to, że stderr jest używany do czytania, ponieważ jest najmniej prawdopodobne, że został przekierowany? Nie widzę żadnej różnicy między tym a standardowym (lub dla tego standardowego przed przekierowaniem).
ctrl-alt-delor
4
Tak, ponieważ jest to deskryptor pliku, który ma najmniejszą szansę na przekierowanie.
schily
@ ctrl-alt-delor: To było / jest typowe dla powłok działających z stdin, stdout i stderr, wszystkie są dup()licencjami tego samego opisu pliku, wszystkie otwarte w tty. (Najwyraźniej POSIX wciąż wymaga lub sugeruje (ta odpowiedź nie mówi), że stderr będzie FD do odczytu / zapisu, a nie otworzy się z czymś podobnym open("/dev/ttyS0", O_WRONLY). Czytanie stderr nie powiedzie się w tym przypadku.)
Peter Cordes