Как `less 'получает данные из stdin, когда все еще может читать команды от пользователя?

47

Как большинство из вас делали много раз, удобно просматривать длинный текст, используя less:

some_command | less

Теперь его стандартный подключен к трубе (FIFO). Как он все еще может читать такие команды, как вверх / вниз / выход?

iBug
источник
15
lessчитает данные для отображения из stdin и читает команды из tty. Это разные вещи.
Уильям Перселл
2
@WilliamPursell Да, я знаю. Но есть только один стандартный поток ввода, верно?
iBug
4
Да, есть один входной поток и один tty. lessчитает данные из stdin и команды из tty.
Уильям Перселл

Ответы:

52

Как упомянул Уильям Перселл , lessсчитывает нажатия клавиш пользователя из терминала. Он явно открывает /dev/ttyуправляющий терминал; это дает ему дескриптор файла, отдельный от стандартного ввода, из которого он может читать интерактивный ввод пользователя. При необходимости он может одновременно считывать данные со стандартного ввода. (Это может также написать напрямую в терминал при необходимости.)

Вы можете увидеть это, запустив

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

Перемещайтесь по входу, выходите lessи смотрите на содержимое less.trace: вы увидите, что оно открыто /dev/tty, и прочитаете как из файлового дескриптора 0, так и из того, который был возвращен при его открытии /dev/tty(вероятно, 3).

Это обычная практика для программ, желающих убедиться, что они читают и пишут в терминал. Одним из примеров является SSH, например, когда он запрашивает пароль или фразу-пароль.

Как пояснил на Шили , если /dev/ttyне может быть открыт, lessбудет считываться из стандартной ошибки (дескриптор файла 2). lessИспользование /dev/ttyбыло введено в версии 177, выпущенной 2 апреля 1991 года.

При попытке запуска cat /dev/tty | less, как и предложил на Хаген фон Eitzen , lessпреуспеет в открытии , /dev/ttyно не получите никакой информации от него до тех пор , catзакрывает его. Таким образом, вы увидите пустой экран, и ничего больше, пока не нажмете, CtrlCчтобы убить cat(или убить его каким-либо другим способом); затем lessпокажет все, что вы ввели во время catработы, и позволит вам контролировать это.

Стивен Китт
источник
4
@HagenvonEitzen Ваш компьютер взорвется! Это похоже на то, как Кирк и Спок заставили андроидов Мадда разбиться.
Бармар
7
@HagenvonEitzen Wow. Вдвойне бесполезное использование кошки . Я впечатлен.
Эндрю Хенле
8
@ Grawity Я думаю, что Эндрю говорит о том, что cat blah |его можно заменить < blah, и даже в этом случае это не нужно, так как less blahработает тоже (хорошо less -f /dev/tty). Но чтение из /dev/ttyэто немного особого случая, и все три варианта ( cat /dev/tty | less, less < /dev/ttyи less -f /dev/tty) дают разные результаты.
Стивен Китт
1
Всегда ли / dev / tty указывает на правильное место? Я думаю, вам обычно нужно использовать / dev / ptsX?
StarWeaver
2
@ StarWeaver увидеть этот вопрос о разнице между /dev/ttyи /dev/pts/....
Стивен Китт
26

UNIX предоставляет два метода для чтения пользовательского ввода при перенаправлении стандартного ввода:

  • Оригинальный метод заключается в чтении из stderr . Stderr открыт для записи и чтения, и это все еще упоминается в POSIX.

  • В более поздних версиях UNIX (около 1979 г.) был добавлен /dev/ttyинтерфейс драйвера, который позволяет открывать управляющий tty процесса. Поскольку существуют процессы без управляющего tty, возможно, что попытка открытия /dev/ttyне удалась. Поэтому дружественно написанное программное обеспечение имеет запасной вариант к исходному методу, а затем пытается прочитать из stderr.

Шили
источник
11
Читать из stderr? Узнал что-то новое.
iBug
1
Я рад, что кто-то помнит старые способы.
Джошуа
3
Является ли причиной того, что stderr используется для чтения, потому что он реже всего перенаправляется? Я не вижу никакой другой разницы между ним и stdout (или для этого mater stdin, до перенаправления).
Ctrl-Alt-Delor
4
Да, это потому, что это дескриптор файла, у которого меньше всего шансов быть перенаправленным.
щили
@ ctrl-alt-delor: типично, что оболочки работают с stdin, stdout и stderr, dup()хотя все они являются лицензиями одного и того же описания файла, но все они открыты на tty. ( По- видимому POSIX все еще требует или предложить (этот ответ не говорит) , что STDERR быть для чтения / записи FD, не открыл что - то вроде open("/dev/ttyS0", O_WRONLY)чтения STDERR потерпит неудачу в этом случае.)
Питер Кордес