Como o `less` pega os dados do stdin e ainda consegue ler os comandos do usuário?

47

Como a maioria de vocês já fez várias vezes, é conveniente exibir textos longos usando less:

some_command | less

Agora seu stdin está conectado a um tubo (FIFO). Como ele ainda pode ler comandos como up / down / quit?

iBug
fonte
15
lesslê os dados a serem exibidos a partir de stdin e lê comandos a partir do tty. São coisas diferentes.
precisa
2
@WilliamPursell Sim, eu sei. Mas há apenas um fluxo de entrada padrão, certo?
iBug
4
Sim, há um fluxo de entrada e um tty. lesslê dados do stdin e comandos do tty.
precisa

Respostas:

52

Conforme mencionado por William Pursell , lesslê as teclas digitadas pelo usuário no terminal. Ele abre explicitamente /dev/tty, o terminal de controle; que fornece um descritor de arquivo, separado da entrada padrão, do qual ele pode ler a entrada interativa do usuário. Ele pode ler dados simultaneamente para exibir sua entrada padrão, se necessário. (Ele também pode gravar diretamente no terminal, se necessário.)

Você pode ver isso acontecendo executando

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

Mova-se pela entrada, saia lesse observe o conteúdo de less.trace: você a verá aberta /dev/ttye lerá o descritor de arquivo 0 e o que foi retornado quando foi aberto /dev/tty(provavelmente 3).

Essa é uma prática comum para programas que desejam garantir que estejam lendo e gravando no terminal. Um exemplo é o SSH, por exemplo , quando solicita uma senha ou frase secreta.

Conforme explicado por schily , se /dev/ttynão puder ser aberto, lessele lerá seu erro padrão (descritor de arquivo 2). lessO uso de /dev/ttyfoi introduzido na versão 177, lançada em 2 de abril de 1991.

Se você tentar executar cat /dev/tty | less, como sugerido por Hagen von Eitzen , lessserá bem-sucedido na abertura, /dev/ttymas não receberá nenhuma entrada até que a catfeche. Então você verá a tela em branco e nada mais até pressionar CtrlCpara matar cat(ou matar de outra maneira); em seguida less, mostrará o que você digitou enquanto catestava em execução e permitirá que você o controle.

Stephen Kitt
fonte
4
@HagenvonEitzen Seu computador irá explodir! É como a maneira como Kirk e Spock fizeram com que os andróides de Mudd quebrassem.
Barmar
7
@HagenvonEitzen Wow. Um uso duplamente inútil de gato . Estou impressionado.
Andrew Henle
8
@rawity Acho que o argumento de Andrew é que cat blah |pode ser substituído por < blah, e mesmo isso é desnecessário neste caso, pois less blahfunciona também (bem less -f /dev/tty). Mas a leitura de /dev/ttyé um caso um pouco especial, e todas as três variantes ( cat /dev/tty | less, less < /dev/ttye less -f /dev/tty) produzem resultados diferentes.
Stephen Kitt
11
/ Dev / tty sempre aponta para o lugar certo de alguma forma? Eu acho que você precisaria usar / dev / ptsX normalmente?
StarWeaver
2
O @ StarWeaver vê esta pergunta sobre a diferença entre /dev/ttye /dev/pts/....
Stephen Kitt
26

O UNIX fornece dois métodos para ler a entrada dos usuários enquanto o stdin foi redirecionado:

  • O método original é ler do stderr . O Stderr está aberto para escrita e leitura e isso ainda é mencionado no POSIX.

  • Versões posteriores do UNIX (por volta de 1979) adicionaram uma /dev/ttyinterface de driver que permite abrir o controle de um processo. Como existem processos sem um controle tty, é possível que uma tentativa de abrir /dev/ttyfalhe. Um software amigável escrito, portanto, tem um retorno ao método original e, em seguida, tenta ler do stderr.

esperto
fonte
11
Leia do stderr? Aprendeu algo novo.
iBug
11
Fico feliz que alguém se lembre dos velhos hábitos.
Joshua
3
É a razão pela qual o stderr é usado para leitura, porque é menos provável que ele tenha sido redirecionado? Não vejo outra diferença entre ele e o stdout (ou para esse mater stdin, antes do redirecionamento).
CTRL-ALT-DELOR
4
Sim, é porque esse é o descritor de arquivo que tem menos chances de ser redirecionado.
schily
@ ctrl-alt-delor: Era / é típico os shells rodarem com stdin, stdout e stderr, todos dup()licenciados da mesma descrição de arquivo, porém, todos abertos no tty. (Aparentemente POSIX ainda requer ou sugerir (esta resposta não diz) que stderr ser uma leitura / gravação FD, não abriu com algo como open("/dev/ttyS0", O_WRONLY)Leitura stderr iria falhar nesse caso..)
Peter Cordes