¿Cómo `less` toma datos de stdin mientras todavía puede leer comandos del usuario?

47

Como la mayoría de ustedes lo han hecho muchas veces, es conveniente ver textos largos usando less:

some_command | less

Ahora su stdin está conectado a una tubería (FIFO). ¿Cómo puede leer comandos como subir / bajar / salir?

iBug
fuente
15
lesslee los datos para mostrar desde stdin y lee los comandos de tty. Son cosas diferentes.
William Pursell
2
@WilliamPursell Sí, lo sé. Pero solo hay un flujo de entrada estándar, ¿verdad?
iBug
44
Sí, hay una secuencia de entrada y una tty. lesslee datos de stdin y comandos de tty.
William Pursell

Respuestas:

52

Como mencionó William Pursell , lesslee las pulsaciones de teclas del usuario desde el terminal. Se abre explícitamente /dev/tty, el terminal de control; eso le da un descriptor de archivo, separado de la entrada estándar, desde el cual puede leer la entrada interactiva del usuario. Puede leer simultáneamente datos para mostrar desde su entrada estándar si es necesario. (También podría escribir directamente en el terminal si es necesario).

Puede ver que esto suceda ejecutando

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

Desplácese por la entrada, salga lessy mire el contenido de less.trace: lo verá abierto /dev/ttyy leerá tanto el descriptor de archivo 0 como el que se devolvió cuando se abrió /dev/tty(probablemente 3).

Esta es una práctica común para los programas que desean asegurarse de que leen y escriben en el terminal. Un ejemplo es SSH, por ejemplo , cuando solicita una contraseña o frase de contraseña.

Como se explicó por schily , si /dev/ttyno se puede abrir, lessleerá de su error estándar (descriptor de archivo 2). lessEl uso de /dev/ttyse introdujo en la versión 177, lanzada el 2 de abril de 1991.

Si intenta ejecutar cat /dev/tty | less, como se sugirió por Hagen von Eitzen , lesstendrá éxito en la apertura /dev/tty, pero no recibirá el aporte de él hasta catla cierra. Entonces verá la pantalla en blanco, y nada más hasta que presione CtrlCpara matar cat(o matarlo de alguna otra manera); luego lessmostrará lo que haya escrito mientras catestaba en ejecución y le permitirá controlarlo.

Stephen Kitt
fuente
44
@HagenvonEitzen ¡Tu computadora explotará! Es como la forma en que Kirk y Spock hicieron que los androides de Mudd se estrellaran.
Barmar
77
@HagenvonEitzen Wow. Un uso doblemente inútil del gato . Estoy impresionado.
Andrew Henle
8
@grawity Creo que el punto de Andrew es que cat blah |puede ser reemplazado por < blah, e incluso eso es innecesario en este caso ya que less blahtambién funciona (bueno, less -f /dev/tty). Pero leer /dev/ttyes un caso un poco especial, y las tres variantes ( cat /dev/tty | less, less < /dev/ttyy less -f /dev/tty) producen resultados diferentes.
Stephen Kitt
1
¿/ Dev / tty siempre apunta al lugar correcto de alguna manera? Creo que necesitarías usar / dev / ptsX por lo general.
StarWeaver 01 de
2
@StarWeaver vea esta pregunta sobre la diferencia entre /dev/ttyy /dev/pts/....
Stephen Kitt
26

UNIX proporciona dos métodos para leer la entrada de los usuarios mientras se ha redirigido stdin:

  • El método original es leer desde stderr . Stderr está abierto para escribir y leer y esto todavía se menciona en POSIX.

  • Las versiones posteriores de UNIX agregaron (alrededor de 1979) una /dev/ttyinterfaz de controlador que permite abrir el control tty de un proceso. Dado que hay procesos sin un tty de control, es posible que un intento de abrir /dev/ttyfalle. El software escrito amigable, por lo tanto, tiene un respaldo al método original y luego intenta leer desde stderr.

astuto
fuente
11
Leer de stderr? Aprendí algo nuevo.
iBug
1
Me alegra que alguien recuerde las viejas formas.
Joshua
3
¿Es la razón por la que stderr se usa para leer, porque es menos probable que haya sido redirigido? No veo ninguna otra diferencia entre él y stdout (o para ese stdin mater, antes de la redirección).
ctrl-alt-delor
44
Sí, es porque este es el descriptor de archivo que tiene menos posibilidades de ser redirigido.
schily
@ ctrl-alt-delor: Era / es típico que los shells se ejecutaran con stdin, stdout y stderr, dup()aunque todos eran licates de la misma descripción de archivo, todos abiertos en el tty. (Aparentemente, POSIX todavía requiere o sugiere (esta respuesta no dice) que stderr sea un FD de lectura / escritura, no abierto con algo como open("/dev/ttyS0", O_WRONLY). La lectura de stderr fallaría en ese caso.)
Peter Cordes