Trata-se do comportamento do \b
caractere backspace ( ). Tenho o seguinte programa em C:
int main() {
printf("Hello\b\b");
sleep(5);
printf("h\n");
return 0;
}
A saída no meu terminal é
Helho
com o cursor avançando para a primeira posição da linha seguinte.
Primeiro, a coisa toda é impressa somente após o sono de 5 segundos, então deduzi que a saída do kernel para o terminal é armazenada em buffer de linha. Então agora minhas perguntas são:
- Como o
\b\b
volta dois espaços, para a posição do (segundo)l
, então semelhante a comol
foi substituído porh
, oo
deveria ter sido substituído por\n
. Por que não foi? - Se eu retirar a linha
printf("h\n");
, ele imprimeHello
e volta dois caracteres, sem apagar. Isso que obtive de outras respostas é por causa de um backspace não destrutivo. Por que esse comportamento é diferente para entrada e saída? Ou seja, se eu inserir algo no terminal (mesmo o mesmo programa) e pressionar Backspace, ele apaga o último caractere, mas não a saída. Por quê?
Estou em um sistema Ubuntu no terminal xterm usando o bash, se isso ajudar.
Não, a saída do seu programa para o kernel é armazenada em buffer de linha. Esse é o comportamento padrão para
stdio
quandostdout
é um terminal. Adicione a chamadasetbuf(stdout, NULL)
para desativar o buffer de saída para arquivosstdout
. Vejasetbuf(3)
.Como o caractere de nova linha apenas move o cursor (e rola a tela), ele não é impresso como um caractere visível que ocuparia o lugar de um glifo no terminal. Se assumirmos que tomaria o lugar de um glifo, como seria?
Bem, o que acontece quando você digita depende do modo em que o terminal está. Grosso modo, pode ser no modo "cozido" usual, onde o próprio terminal fornece edição de linha elementar (lida com backspaces); ou em um modo "bruto", onde todos os pressionamentos de tecla vão para o aplicativo e cabe ao aplicativo decidir o que fazer com eles e o que gerar em resposta. O modo cozido geralmente acompanha o "eco local", onde o terminal (local para o usuário) imprime os caracteres à medida que são digitados. No modo raw, o aplicativo geralmente se encarrega de ecoar os caracteres digitados, para ter controle total sobre o que está visível.
Veja, por exemplo, esta questão para discussão sobre os modos de terminal: Qual é a diferença entre um driver de dispositivo “bruto” e um “cozido”?
Se você executar, por exemplo
cat
, o terminal estará no modo cozido (o padrão) e lidará com a edição da linha. Bater, por exemplo xBackspaceCtrl-D, resultarácat
apenas na leitura da entrada vazia, sinalizando o fim da entrada. Você pode verificar isso comstrace
. Mas se você executar um shell Bash interativo, ele manipulará o backspace por si só e exibirá o que considerar apropriado para fazer o que o usuário espera, ou seja, limpar um caractere.Aqui está parte da saída para
strace -etrace=read,write -obash.trace bash
, após inserir a sequência mencionada xBackspaceCtrl-D:Primeiro, Bash
read
s ewrite
s thex
, enviando-o para o terminal. Em seguida, ele lê o backspace (código de caractere 0177 em octal ou 127 em decimal) e emite o caractere de backspace (octal 010, decimal 8 (*) ) que move o cursor para trás e gera a sequência de controle para limpar o final da linha,<ESC>[K
. O último\4
é oCtrl-D
, que é usado pelo Bash para sair do programa.(* na entrada,
Ctrl-H
teria o código de caractere decimal 8. Backspace é o mesmo ou 127 aqui, dependendo novamente de como o terminal está configurado.)Em comparação, o mesmo experimento
cat
mostra apenas uma única leitura de zero bytes, a condição de "fim do arquivo". O fim do arquivo pode significar um canal ou soquete conectado sendo fechado, um final real do arquivo ouCtrl-D
recebido de um terminal no modo cozido:Em particular,
cat
não vê ox
, nem o backspace, nem o código real paraCtrl-D
: eles são manipulados pelo terminal. Que pode ser o driver de terminal virtual no kernel; um terminal físico real em uma conexão serial ou algo semelhante; ou um emulador de terminal como xterm rodando na mesma máquina ou na extremidade remota de uma conexão SSH. Não importa para o software userspace.Os códigos (
\b
,\n
etc) movem o cursor, não o texto.Acho que você descobrirá que o comportamento vem dos 'bons velhos tempos', quando tínhamos que manipular o cursor em uma impressora para obter efeitos impressionantes, como fontes em negrito e tachado em teletipo, impressoras de bolas de golfe e similares.
\b
apenas move o ponto de inserção para trás e permite sobrescrever. Em uma tela digital, isso exclui o conteúdo anterior, mas em uma impressora antiga, você obteria o caractere sobreposto.\n
move o cursor para uma nova linha, mas deixa o texto na linha antiga para trás. Isso teria sido um CRLF que coloca o cursor de volta no início da linha e informa à impressora para rolar uma linha para cima.Os códigos CR, LF e CRLF estão novamente ligados à necessidade de manipular dispositivos antigos para obter esses efeitos modernos.
..... e não havia como excluir caracteres de uma página impressa. Esperava-se que você acertasse antes de postar para imprimir.
EDITAR
Re ponto 2. Entrada e saída fazem coisas diferentes porque você está mandando.
\b
é equivalente à seta para a esquerda com INSERT desligado, não delete.\n
é equivalente à seta para baixo (LF) e HOME (CR), não à tecla 'enter'.Em primeiro lugar, se você quiser aprender sobre essas coisas, você deve ler
Você pergunta,
Aqui estão algumas curiosidades para você: Nos primeiros dias do Unix (década de 1970, antes mesmo de o Linux existir), o backspace 1 não apagava os caracteres de entrada e os usuários odiavam. Você digitaria
beast
, pressionaria Backspace três vezes e ainda teria um olhar malignobe|ast
olhando para você (|
representa o cursor). Mesmo depois de digitarst
(porque você quis dizerbest
o tempo todo), a tela ainda diziabest|t
, e era confuso.Muitos usuários adotaram o hábito de digitar Backspace Space Backspace uma vez para cada caractere que desejavam apagar, a fim de usar os espaços para substituir/destruir os caracteres ruins; ou seja, para apagá -los manualmente . Eventualmente (1979 ou início dos anos 1980), os desenvolvedores cederam e tornaram isso automático:
onde “Ctrl-H” é o código de controle para um backspace.
Em algumas situações, você pode desativar essa opção e obter o comportamento antigo digitando
stty -echoe
(echoe
= “apagar eco” e-
significa “não faça isso”).____
1 Na verdade, bem no início, backspace não era Backspace — mas isso é outra história.