Caracteres de controle ASCII no meu terminal
Olá! Eu tenho pensado muito no terminal e ontem fiquei curioso sobre todos esses “códigos de controle”, como Ctrl-A
Assim, Ctrl-C
Assim, Ctrl-W
etc. Qual é o problema com todos eles?
Uma tabela de caracteres de controle ASCII
Aqui está uma tabela de todos os 33 caracteres de controle ASCII e o que eles fazem na minha máquina (no Mac OS), mais ou menos. Existem cerca de um milhão de advertências, mas vou falar sobre o que isso significa e todos os problemas com este diagrama que eu conheço.
Você também pode visualizá -lo como uma página HTML (acabei de fazer uma imagem para que ela aparecesse no RSS).
Diferentes tipos de códigos são misturados
A primeira coisa surpreendente sobre esse diagrama para mim é que existem 33 códigos de controle, divididos em (muito falando) essas categorias:
- Códigos que são tratados pelo driver de terminal do sistema operacional, por exemplo, quando o sistema operacional vê um
3
(Ctrl-C
), ele enviará umSIGINT
sinal para o programa atual - Todo o resto é passado para o aplicativo AS-IS e o aplicativo pode fazer o que quiser com eles. Algumas subcategorias daquelas:
- Códigos que correspondem a uma tecla literal de uma chave no teclado (
Enter
Assim,Tab
Assim,Backspace
). Por exemplo, quando você pressionaEnter
seu terminal é enviado13
. - Códigos usados por
readline
: “O aplicativo pode fazer o que quiser” geralmente significa “fará mais ou menos o que oreadline
biblioteca, se o aplicativo realmente usareadline
ou não ”, então eu rotulei um monte de códigos quereadline
usos - Outros códigos, por exemplo, eu acho
Ctrl-X
não tem significado padrão no terminal em geral, mas o Emacs o usa muito fortemente
- Códigos que correspondem a uma tecla literal de uma chave no teclado (
Não existe uma estrutura real para quais códigos estão em quais categorias eles são apenas espalhados aleatoriamente porque isso evoluiu organicamente.
(Se você está curioso sobre o ReadLine, escrevi mais sobre o ReadLine ao inserir texto no terminal é complicado, e há muitas folhas de trapaça por aí)
Existem apenas 33 códigos de controle
Outra coisa que acho um pouco surpreendente é que são apenas 33 códigos de controle – A a Z, mais 7 mais (@, (, \, ), ^, _, ?
). Isso significa que, se você quiser ter, por exemplo Ctrl-1
Como atalho de teclado em um aplicativo de terminal, isso não é realmente significativo – no meu pela máquina, pelo menos Ctrl-1
é exatamente a mesma coisa que apenas pressionar 1
Assim, Ctrl-3
é o mesmo que Ctrl-(
etc.
Também Ctrl+Shift+C
Não é um código de controle – o que ele faz depende do seu emulador de terminal. No Linux Ctrl-Shift-X
é frequentemente usado pelo emulador de terminal para copiar ou abrir uma nova guia ou colar, por exemplo, ele não é enviado para o TTY.
Também eu uso Ctrl+Left Arrow
O tempo todo, mas isso não é um código de controle, em vez disso, envia uma sequência de escape ANSI (ctrl-((1;5D
) que é uma coisa diferente para a qual absolutamente não temos espaço neste post.
Essa coisa de “há apenas 33 códigos” é totalmente diferente de como os atalhos do teclado funcionam em uma GUI onde você pode ter Ctrl+KEY
Para qualquer chave que você quiser.
Os nomes oficiais da ASCII não são muito significativos para mim
Cada um desses 33 códigos de controle tem um nome em ASCII (por exemplo 3
é ETX
). Quando todos esses códigos de controle foram originalmente definidos, eles não estavam sendo usados para computadores ou terminais, eles foram usados para a máquina Telegraph. As máquinas de telégrafo não são as mesmas que os terminais do UNIX, então muitos códigos foram reaproveitados para significar outra coisa.
Pessoalmente, não acho esses nomes ASCII muito úteis, porque 50% das vezes o nome no ASCII não tem uma relação real com o que esse código faz nos sistemas Unix hoje. Portanto, é mais fácil ignorar completamente os nomes ASCII, em vez de tentar descobrir quais ainda correspondem ao seu significado original.
É difícil usar o Ctrl-M como um atalho de teclado
Outra coisa que é um pouco estranha é que Ctrl-M
é literalmente o mesmo que
Enter
e Ctrl-I
é o mesmo que Tab
o que dificulta o uso desses dois como atalhos de teclado.
De algumas pesquisas rápidas, parece que algumas pessoas ainda usam Ctrl-I
e
Ctrl-M
Como atalhos de teclado (aqui está um exemplo), mas para fazer isso, você precisa configurar seu emulador de terminal para tratá -los de maneira diferente do padrão.
Para mim, o principal argumento é que, se eu escrever um aplicativo de terminal, devo evitar Ctrl-I
e Ctrl-M
como atalhos de teclado nele.
Como identificar quais códigos de controle são enviados
Enquanto escrevia isso, eu precisava fazer um monte de experimentos para descobrir o que várias combinações importantes fizeram, então escrevi este script python eco-key.py que as imprimirá.
Provavelmente há uma maneira mais oficial, mas eu apreciei ter um script que eu poderia personalizar.
Advertência: no modo canônico vs não canônico
Dois desses códigos (Ctrl-W
e Ctrl-U
) são rotulados na tabela como “tratados pelo sistema operacional”, mas na verdade eles não são sempre Mandido pelo sistema operacional, depende se o terminal está no modo “canônico” ou no “modo não canônico”.
No modo canônico, os programas só recebem entrada quando você pressiona Enter
(e o sistema operacional é responsável por excluir caracteres quando você pressiona Backspace
ou Ctrl-W
). Mas no modo não canônico, o programa recebe entrada imediatamente quando você pressiona uma tecla e o Ctrl-W
e Ctrl-U
Os códigos são passados para o programa para lidar com a maneira como desejar.
Geralmente no modo não canônico, o programa lida Ctrl-W
e Ctrl-U
Da mesma forma como o sistema operacional, mas existem algumas pequenas diferenças.
Alguns exemplos de programas que usam o modo canônico:
- provavelmente praticamente qualquer programa não interativo, como
grep
oucat
git
Eu penso
Exemplos de programas que usam modo não canônico:
python3
Assim,irb
e outros repls- sua concha
- qualquer tui de tela inteira gosta
less
ouvim
Advertência: todos os códigos do “driver do terminal do sistema operacional” são configuráveis com stty
Eu disse isso Ctrl-C
envia SIGINT
Mas tecnicamente isso não é necessariamente verdadeiro, se você realmente deseja, pode remapear todos os códigos rotulados como “Driver do terminal do sistema operacional”, além de backspace, usando uma ferramenta chamada stty
e você pode ver os mapeamentos com stty -a
.
Aqui estão os mapeamentos da minha máquina agora:
$ stty -a
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;
eol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp = ^Z; time = 0; werase = ^W;
Pessoalmente, nunca remaguei nada disso e não consigo imaginar uma razão pela qual eu (acho que seria uma receita de confusão e desastre para mim), mas perguntei ao Mastodon e as pessoas disseram que os motivos mais comuns que usaram usaram
stty
eram:
- consertar um terminal quebrado com
stty sane
- definir
stty erase ^H
Para mudar como funciona o backspace - definir
stty ixoff
- Algumas pessoas até mapeiam
SIGINT
para uma chave diferente, como o delesDELETE
chave
Advertência: em sinais
Duas advertências de sinais:
- Se o
ISIG
O modo terminal está desligado e o sistema operacional não enviará sinais. Por exemplovim
desligaISIG
- Aparentemente no BSDS, há um código de controle extra (
Ctrl-T
) que enviaSIGINFO
Você pode ver quais modos de terminal um programa está definindo usando strace
assim, os modos de terminal são definidos com o ioctl
Chamada do sistema:
$ strace -tt -o out vim
$ grep ioctl out | grep SET
Aqui estão os modos vim
define quando começa (ISIG
e ICANON
estão faltando!):
17:43:36.670636 ioctl(0, TCSETS, {c_iflag=IXANY|IMAXBEL|IUTF8,
c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST, c_cflag=B38400|CS8|CREAD,
c_lflag=ECHOK|ECHOCTL|ECHOKE|PENDIN, ...}) = 0
e redefine os modos quando sai:
17:43:38.027284 ioctl(0, TCSETS, {c_iflag=ICRNL|IXANY|IMAXBEL|IUTF8,
c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD,
c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE|PENDIN, ...}) = 0
Eu acho que a combinação específica de modos que Vim está usando aqui pode ser chamada de “modo bruto”, o Man CfmakerAw fala sobre isso.
Existem muitos conflitos
Relacionado a “Existem apenas 33 códigos”, existem muitos conflitos em que diferentes partes do sistema desejam usar o mesmo código para coisas diferentes, por exemplo, por padrão Ctrl-S
irá congelar sua tela, mas se você desligar isso então readline
vai usar Ctrl-S
Para fazer uma pesquisa a termo.
Outro exemplo é que às vezes na minha máquina Ctrl-T
Vai enviar SIGINFO
E às vezes transporá 2 caracteres e às vezes fará algo completamente diferente, dependendo de:
- se o programa tem
ISIG
definir - Se o programa usa
readline
/ imita o comportamento da readline
Advertência: no “backspace” e “outro backspace”
Neste diagrama, rotulei o código 127 como “backspace” e 8 como “outro backspace”. Uh, o quê?
Eu acho que este foi o maior tópico de discussão nas respostas sobre Mastodon – aparentemente há muita história nisso e nunca tinha ouvido falar disso antes.
Primeiro, eis como funciona na minha máquina:
- Eu pressiono o
Backspace
chave - O TTY é enviado o byte
127
que é chamadoDEL
em ASCII - o driver do terminal do sistema operacional e o readline têm
127
mapeado para o “backspace” (para que funcione no modo canônico e no modo não canônico) - O personagem anterior é excluído
Se eu pressionar Ctrl+H
tem o mesmo efeito que Backspace
Se estou usando o ReadLine, mas em um programa sem suporte à readline (como cat
por exemplo), ele apenas imprime ^H
.
Aparentemente, a etapa 2 acima é diferente para algumas pessoas – seus Backspace
Key envia o byte 8
em vez de 127
e se eles querem que o backspace funcione, precisam configurar o sistema operacional (usando stty
) para definir erase = ^H
.
Há uma seção incrível do Manual de Política Debian na configuração do teclado que descreve como Delete
e Backspace
deve funcionar de acordo com a política do Debian, que parece muito semelhante à forma como funciona no meu Mac hoje. Meu entendimento (através deste post Mastodon) é que essa política foi escrita nos anos 90, porque havia muita confusão sobre o que Backspace
Deveria fazer nos anos 90 e precisava haver um padrão para fazer tudo funcionar.
Há um monte de coisas terminais mais históricas aqui, mas é tudo o que vou dizer por enquanto.
Provavelmente há muito mais diversidade em como isso funciona
Provavelmente perdi um monte de mais maneiras de que “como funciona na minha máquina” pode ser diferente de como funciona nas máquinas de outras pessoas, e provavelmente cometi alguns erros sobre como isso também funciona na minha máquina. Mas isso é tudo o que tenho hoje.
Mais algumas coisas que sei que deixei de fora: de acordo com stty -a
Ctrl-O
é “descarte”, Ctrl-R
é “reimpressão” e Ctrl-Y
é “dsusp”. Eu não tenho idéia de como fazer isso realmente fazer qualquer coisa (pressioná -los não faz nada óbvio, e algumas pessoas me disseram o que costumavam fazer historicamente, mas não está claro para mim se tiverem um uso em 2024), e na maioria das vezes na prática eles parecem ser transmitidos para o aplicativo de qualquer maneira, então eu apenas rotulou Ctrl-R
e Ctrl-Y
como
readline
.
Nem tudo isso é útil para saber
Também quero dizer que acho que o conteúdo deste post é meio interessante, mas não acho que sejam necessariamente que útil. Eu usei o terminal com bastante sucesso todos os dias nos últimos 20 anos sem saber literalmente nada disso – eu apenas sabia o que Ctrl-C
Assim, Ctrl-D
Assim, Ctrl-Z
Assim, Ctrl-R
Assim,
Ctrl-L
fez na prática (mais talvez Ctrl-A
Assim, Ctrl-E
e Ctrl-W
) e não se preocupei com os detalhes na maior parte, e isso quase sempre era totalmente bom, exceto quando eu estava tentando usar o XTERM.JS.
Mas eu me diverti aprendendo sobre isso, então talvez seja interessante para você também.