- Editar texto na linha de comando parece uma função básica, mas cada programa trata a entrada de um jeito diferente, então a mesma tecla pode funcionar de forma totalmente diferente
- A experiência de entrada no terminal geralmente se divide em quatro caminhos: sem sistema de entrada, GNU readline, bibliotecas alternativas como
libedit e sistemas de entrada próprios
- Em ferramentas simples como
cat, nc e dash, as teclas de seta podem aparecer como ^[[D, mas os controles fornecidos pelo terminal como Ctrl+W, Ctrl+U, Ctrl+C e Ctrl+Z ainda funcionam
- Ferramentas baseadas em readline como
bash, psql, irb e python3 oferecem histórico, teclas de seta, busca com Ctrl+R e navegação com Ctrl+A/Ctrl+E, e é possível adicionar uma experiência parecida com rlwrap
- Identificar qual sistema de entrada está em uso ajuda a entender rapidamente por que as teclas de seta ou
Ctrl+R se comportam de outro jeito e, se necessário, escolher entre consultar a documentação ou usar alternativas como rlwrap
A experiência de entrada muda de programa para programa
- A forma de digitar e editar texto no terminal varia bastante conforme o programa
- Os tipos mais comuns podem ser divididos assim
cat, nc, git commit --interactive e outros não oferecem suporte às teclas de seta, então você pode ver sequências como ^[[D^[[D^[[C^[[C^
irb, python3 no Linux e outros oferecem funções básicas de edição, como histórico e teclas de seta, por meio da biblioteca readline
- Em alguns casos, como o
/usr/bin/python3 do Mac, as teclas de seta funcionam, mas Ctrl+left ou a busca reversa com Ctrl+R não são suportados
fish, ipython3, micro, vim e outros usam sistemas de entrada próprios
Modo 1: estado básico sem sistema de entrada
- Quando um programa apenas recebe texto com chamadas como
fgets() e não oferece recursos extras de edição, as teclas de seta podem ser exibidas como sequências de escape em vez de comandos de edição
- Por exemplo, ao pressionar a seta para a esquerda no
dash, ^[[D pode aparecer no terminal assim
$ ls l-^[[D^[[D^[[D
- Ainda assim, os controles básicos fornecidos pelo terminal continuam disponíveis
- Entrada de texto
- Backspace
Ctrl+W: apagar a palavra anterior
Ctrl+U: apagar a linha inteira
Ctrl+C: interromper o processo
Ctrl+Z: suspender o processo
- Mesmo nesse ambiente, usar
Ctrl+W permite apagar por palavra sem precisar apertar backspace várias vezes
- Os códigos de controle suportados pelo terminal podem ser vistos com
stty -a
Modo 2: ferramentas que usam readline
- readline é uma biblioteca GNU que torna a entrada de texto mais prática e é usada por várias ferramentas interativas
- Alguns atalhos comuns do readline são
Ctrl+E ou End: ir para o fim da linha
Ctrl+A ou Home: ir para o começo da linha
Ctrl+left/right arrow: mover uma palavra para trás ou para frente
- Seta para cima: ir para o comando anterior
Ctrl+R: buscar no histórico
- Os atalhos
Ctrl+W e Ctrl+U do modo básico também podem ser usados, mas no readline o Ctrl+U apaga do cursor até o início da linha, e não a linha inteira
- A busca no histórico com
Ctrl+R do bash é uma função fornecida pelo readline
psql, irb e python3 também são exemplos de programas que usam readline
- A lista completa dos comandos de edição do readline está no manual do readline
Adicionando rlwrap a ferramentas sem readline
- Mesmo programas que não suportam readline, como
nc, podem ser usados como se suportassem ao serem executados com rlwrap nc
- O rlwrap pode melhorar bastante a usabilidade de ferramentas sem readline
- Também é possível configurar autocompletar personalizado, embora isso não tenha sido testado na prática aqui
Por que algumas ferramentas não usam readline
- Há alguns motivos para certas ferramentas não usarem readline
- Programas muito simples, como
cat e nc, podem não querer adicionar uma dependência relativamente grande
- O readline usa licença GPL, e não LGPL, então pode não ser compatível com a licença do programa
- Se apenas parte do programa for interativa e a entrada se limitar a um único caractere como
y ou n, o suporte a readline pode ter baixa prioridade
- O
git tem funções interativas como git add -p, mas quando é necessária uma entrada longa e significativa, normalmente ele abre um editor de texto
- O
idris2 não usa readline para manter o mínimo de dependências e sugere usar rlwrap quando recursos interativos melhores forem necessários
Como verificar se a ferramenta atual usa readline
- A forma mais simples de verificar é apertar
Ctrl+R
- Se aparecer o prompt abaixo, é bem provável que a ferramenta esteja usando readline
(reverse-i-search)`':
- Outras bibliotecas também podem usar a mesma expressão, então isso não é uma prova definitiva, mas não se conhece outro sistema que use
reverse-i-search para busca no histórico
Atalhos do readline e Emacs
- Os atalhos
Ctrl+A para ir ao início da linha e Ctrl+E para ir ao fim parecem vir do Emacs
- No Emacs,
Ctrl+A e Ctrl+E fazem a mesma coisa que no readline
- Já
Ctrl+W e Ctrl+U não têm o mesmo comportamento no Emacs e no terminal
- A história do projeto readline pode ser vista em História do projeto Readline
Modo 3: bibliotecas alternativas de entrada, como libedit
- O
/usr/bin/python3 do Mac fica num meio-termo, com suporte apenas parcial aos recursos do readline
- Por exemplo, ao pressionar
Ctrl+left arrow, em vez de mover por palavra pode aparecer ;5D
$ python3
>>> importt subprocess;5D
- Na instalação padrão de Python do Mac OS, o módulo
readline do Python é na prática baseado em libedit
- Isso provavelmente acontece porque o readline usa licença GPL
- Você pode verificar se esse Python usa libedit com o comando abaixo
$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.
- Em geral, o Python instalado no Linux ou via Homebrew usa readline
- O Python 3.13 pretende remover a dependência de readline e usar uma biblioteca própria, então no futuro a frase “Python usa readline” pode deixar de ser sempre verdadeira
Modo 4: sistema de entrada próprio
- Alguns programas usam seu próprio sistema de edição de texto e às vezes oferecem mais recursos do que o readline
- Exemplos de programas com sistema de entrada próprio incluem
- a maioria dos editores de texto de terminal, como
nano, micro, vim e emacs
- shells como
fish
- o editor de linha zle do
zsh
- REPLs como
ipython
- ferramentas como
atuin
- Nesses sistemas, é possível encontrar recursos como
- autocompletar mais adaptado à ferramenta
- melhor gerenciamento de histórico, inclusive com realce de sintaxe
- mais atalhos de teclado
Sistemas próprios inspirados no readline
- O Atuin é uma ferramenta de busca no histórico do shell; sua implementação de entrada é própria, mas inspirada no readline
- O Atuin aproveita o fato de que muitos usuários já estão acostumados com os atalhos do readline e faz esses atalhos funcionarem mesmo sem usar o readline diretamente
- O prompt_toolkit usado pelo IPython também oferece várias opções, incluindo atalhos no estilo vi
- O padrão do prompt_toolkit é oferecer suporte a atalhos no estilo readline
- Isso é parecido com muitos programas que suportam os atalhos básicos do vim, usando
j para descer e k para subir
- Por exemplo, o Fastmail não tem a maioria dos atalhos fortemente ligados ao vim, mas ainda assim suporta
j e k
- Sistemas próprios inspirados no readline podem ter incompatibilidades sutis com ele, mas isso pode não ser um grande problema para quem usa apenas alguns atalhos básicos
- Em geral, esses sistemas próprios podem oferecer autocompletar melhor do que usar apenas readline
Shells com suporte a modo vi
bash, zsh e fish oferecem suporte ao modo vi para entrada de texto
- Em uma enquete não científica feita no Mastodon, 12% responderam que usam modo vi
- O próprio readline também tem modo vi, e é assim que o suporte ao modo vi no
bash funciona
- Muitos outros programas que usam readline também podem ter modo vi
Fluxo para diagnosticar o estado da entrada
- Saber qual sistema de entrada está em uso torna o comportamento da linha de comando mais previsível
- Em um prompt de entrada, dá para avaliar na seguinte ordem
- Se as teclas de seta não funcionam, provavelmente não há sistema de entrada; ainda assim
Ctrl+W e Ctrl+U podem ser usados, e se necessário é possível adicionar rlwrap
- Se ao usar
Ctrl+R aparece reverse-i-search, provavelmente é readline, e você pode contar com os atalhos conhecidos do readline e com os recursos básicos de histórico
- Se
Ctrl+R faz outra coisa, provavelmente é uma biblioteca de entrada própria; em geral ela pode se comportar de forma parecida com readline, e vale consultar a documentação se necessário
- Ainda restam outras complexidades de entrada que não foram tratadas aqui
- problemas relacionados a
ssh e tmux
- a variável de ambiente
TERM
- diferenças de suporte a copiar e colar entre terminais como GNOME Terminal, iTerm e xterm
- Unicode
- vários outros problemas relacionados à entrada
1 comentários
Opiniões do Hacker News
Os textos da Julia são sempre bons. Algo que ficou de fora: em scripts de shell, usar
sttypermite mudar vários comportamentos do terminal, incluindo a forma de processamento da entradaUm experimento que fiz tempos atrás com
shestty: https://gist.github.com/alganet/63f1dbc97b8fd35f7bb14ec30f79...Em terminais compatíveis com VT100 (mintty, xterm, Terminal.app, vscode etc.), dá para capturar e interpretar dentro do shell a maioria das combinações de teclas e até gestos do mouse (pressionar/arrastar/soltar)
A demo executável é
bash -c "$(curl -L https://git.io/fjToH)"; basta pressionar teclas ou mover o mouse. Para sair, Ctrl+W. Também há suporte a zsh e ksh, mas não a dash e outros que não têmread -rn1Além disso, ao canalizar um programa interativo para
cat -v, como emvi | cat -v, dá para ver os escapes VT100 que esse programa usa; aprendi muito observando como o vi fazia issostty sanepara restaurá-loAbra 3 terminais e execute 1)
man ascii, 2)nc -lvp 9001 | xxd -c1, 3)stty raw -echo; nc -nv 127.0.0.1 9001; depois, no terminal 3, tente reproduzir em ordem toda a coluna “Hex” da manpageDá para observar que alguns valores podem ser gerados de várias formas, e que algumas outras teclas enviam payloads de vários bytes
sttye o modo raw: https://viewsourcecode.org/snaptoken/kilo/, https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode...Os parâmetros e símbolos de
sttyque parecem mágica vêm de sequências de controle definidas há muito tempo. Padrões obscuros dos anos 1970, como ECMA-48, também têm muitos termos difíceis que hoje quase não são usadosPor exemplo, Select Graphic Rendition(SGR) é mais ou menos como uma versão dos anos 1970 de tags HTML; para definir a cor de primeiro plano, usa-se algo como
ESC[38;2;R;G;BmTextESC[0m.ESC[é CSI,mé SGR,38é a cor de primeiro plano,2é o espaço de cores RGB, e0é a redefinição do estiloComo os parâmetros vêm antes do comando, a estrutura parece um pouco invertida. Assim como navegadores, terminais também variavam — e ainda variam — em escopo e forma de implementação. Cores têm suporte bastante bom, mas outras funções nem tanto. Por exemplo,
SGR<7>seria o modo “negative image”, mas o padrão não explica em detalhes; então alguns terminais exibem em reverso, outros tratam de outra forma, e isso ainda causa problemasHá mais de 20 anos, criei uma máquina de estados sobre readline para que ela pudesse ser usada como um editor multilinha de verdade, com navegação para cima e para baixo, não apenas uma linha quebrando visualmente
Vídeo: https://github.com/colmmacc/jot/raw/master/jot-demo.mp4
Repositório CVS do cliente de IM para terminal Unix que incluía esse recurso: https://c-hey.redbrick.dcu.ie/src/c-hey_cvs_latest/
Ao mover para cima e para baixo entre linhas, ele rastreia e redesenha o cursor; quando o tamanho do terminal muda, observa SIGWINCH e redesenha. Queria reescrever em Rust e publicar como uma pequena biblioteca, mas nunca tive tempo. Sempre me surpreendeu que ninguém tenha feito algo parecido
Depois de
autoload -Uz zed, usezedpara editar um arquivo temporário e passá-lo agit commit -F; assim é possível iniciar um editor inline com todos os recursos do ZLE. Salve e faça o commit comC-x C-w, ou cancele comC-cA vantagem dessa implementação básica é que
zedé um novo widget ZLE, então é possível controlar completamente o keymap. O modo emacs ou vi funciona de imediato e também pode ser mais customizadoSe você escrever seu próprio widget ZLE, pode chamá-lo diretamente dentro do editor de linha, eliminando a necessidade de envolver o comando como acima
https://github.com/zsh-users/zsh/blob/master/Functions/Misc/...
git commit. O efeito de “máquina de escrever inline” também é legalEntre as coisas que ficaram de fora do texto estão caracteres largos, o problema de a mesma tecla ser representada por sequências de escape ANSI diferentes por causa de diferenças nos modos de teclado, diferenças de estado de TTY como eco local, e o fato de que as chamadas de sistema que alteram o estado do TTY variam sutilmente entre sistemas operacionais.
O suporte à emulação de terminal também varia bastante. Hoje em dia, em geral, todo mundo imita o xterm, mas mesmo dentro disso ninguém mira 100% de compatibilidade.
Também falta consenso sobre como verificar os recursos oferecidos pelo terminal. Alguns enviam sequências de escape ANSI e aguardam uma resposta, outros olham para
$TERM, e alguns terminais se expõem como xterm, mas ignoram sequências de recursos VT e definem outras variáveis de ambiente. Sinceramente, é mais bagunçado que uma string de user agent.Tudo isso assumindo sistemas POSIX; quando se mistura Windows, fica duas vezes mais interessante.
Se, no bash, você definir
$EDITORpara o seu editor favorito, pode enviar a linha atual para o$EDITORcom Ctrl-X Ctrl-E. Depois de editar o comando, salvar e sair, ele será executado.v.fcfaz a mesma coisa. É só digitarfc, sem sequência com a tecla Ctrl.Também dá para abrir o comando anterior em um editor específico em vez do
$EDITORpadrão. Por exemplo, seEDITOR=vie você quiser editar o comando imediatamente anterior no ed, usefc -e ed.Também há uma forma de criar rapidamente um script de shell a partir do histórico de comandos no modo vi. Se o comando desejado for o 15º no histórico, dá para fazer algo como
fc 15, depoisw1.sh,%d,wq.EDITOR=nvimjunto com copilot.vim como se fosse autocomplete de shell com IA. É bem bom.Vi aqui: https://twitter.com/arjie/status/1575201117595926530?s=46&t=...
Do ponto de vista de um iniciante em Linux, fico curioso sobre como, mesmo quando um programa só lê texto com algo como
fgets(), recursos como backspace, Ctrl+W e Ctrl+U surgem de graça.A documentação de
fgets()diz que ele lê até encontrar uma quebra de linha ou EOF; então fico me perguntando se isso significa que, por padrão, ele fica bloqueado até o usuário inserir uma quebra de linha e, antes disso, o terminal permite editar o buffer da linha com backspace ou Ctrl+W/Ctrl+U.Mas não há garantia de que o programa leia sempre uma linha inteira, e ele também pode ler caractere por caractere passando
countcomo 2. Nesse caso, se ocorrer um backspace, não seria possível “desfazer” um caractere que já foi lido; então fico curioso se há alguma mágica no buffer interno defgets()ou se, nesse caso, o recurso de edição de linha simplesmente quebra.https://en.cppreference.com/w/c/io/fgets
O que parece ser buffering por quebra de linha, na prática, é gerenciado pelo shell; por isso o desenvolvedor pode implementar recursos de manipulação de buffer de texto, como autocompletar com Tab ou adicionar sudo no início da linha.
Antes de pressionar Enter, os caracteres na tela estão apenas na memória do terminal, e a chamada de sistema
readda aplicação fica bloqueada no descritor de arquivo do terminal. Até a linha estar completa, o terminal não escreve dados.Nesse momento, a edição da linha em entrada normalmente também é fornecida pelo próprio terminal. O fato de os caracteres aparecerem na tela é uma função do terminal chamada eco, e o mesmo vale para backspace, delete, Ctrl+C para SIGINT e Ctrl+D para EOF.
Editores de linha e editores de texto desativam esses recursos e colocam o terminal em modo raw. Tudo o que é digitado é enviado imediatamente à aplicação, que trata diretamente. Um exemplo comum é um prompt de senha que desativa apenas o eco para ocultar os caracteres.
O fim de linha tradicional do Unix,
\n, também é, na verdade, uma função do terminal. Do ponto de vista do terminal,\nsó move o cursor uma linha para baixo, então também é necessário\rpara voltar ao início da linha. O verdadeiro fim de linha é\r\n, e o terminal converte\ninvisivelmente para\r\n. Isso também pode ser desativado.No modo raw, não funciona assim.
helpdo VMS para SunOS. O primeiro obstáculo foi descobrir como responder a cada tecla pressionada sem esperar Enter/Return.Meu chefe, de propósito, quase não me ajudou, e me fez descobrir praticamente sozinho em 1986, quando não havia web e quase não havia livros da O’Reilly. Até hoje sou eternamente grato por essa decisão.
O shell dash também oferece suporte a modo de edição se for compilado com libedit. Derivados do Debian provavelmente não fazem isso por questão de espaço, mas, para que quem começou com bash precise aprender menos depois, acho que o certo seria dar suporte.
O padrão POSIX também define
set -o vicomo uma extensão opcional. Se um shell que afirma ser compatível com POSIX implementar esse modo de edição, ele deve obrigatoriamente dar suporte aset -o vi.“
[set -o] vi: permite a edição da linha de comando do shell usando o editor vi embutido. Ao ativar o modo vi, outros modos de edição de linha de comando fornecidos como extensões da implementação devem ser desativados.”https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...
Sempre achei que o terminal é o fator único que mantém o Linux preso para sempre a uma participação de mercado abaixo de 5%
O problema não é só a entrada de texto; a experiência inteira é complexa. 95% da população só quer pilotar um drone, mas recebe na cara a cabine de um Concorde sem nenhum rótulo
https://qph.cf2.quoracdn.net/main-qimg-2566f4c91b894e4169d77..., https://media.thedroningcompany.com/images/tincy/WQZpC56vqMp...
Há uma cabine de Concorde por trás do Ubuntu, e eu até agradeço por poder acessá-la no meu laptop Ubuntu de trabalho. Acho que eu não gostaria se não tivesse esse nível de controle
Por outro lado, o laptop da minha mãe também é Ubuntu, e o da minha namorada é Manjaro; as duas usam bem, só “pilotando o drone”, sem nunca pôr os pés na cabine do Concorde. Claro, eu uso Arch
O terminal é uma ferramenta para desenvolvedores e administradores de sistemas. Só parece diferente porque a maior parte dos usuários de GNU/Linux no desktop é formada por desenvolvedores e administradores de sistemas, e eles são alguns poucos por cento da população
O lugar em que é preciso conhecer um monte de atalhos estranhos é, na verdade, o terminal do macOS, onde tudo foi feito para ser o menos intuitivo possível
Talvez seja uma opinião impopular, mas no iTerm2 mudei para o preset Natural text editing para alinhar com os atalhos tradicionais de edição do macOS
Como o app de terminal normalmente remapeia isso de volta para sequências de controle, há a vantagem de obter a funcionalidade em quase qualquer lugar, sem precisar mudar em vários lugares nem se preocupar com suporte do readline
Não é perfeito, e às vezes sequências que eu precisava digitar manualmente eram remapeadas, mas, por bater com a memória muscular, passei a usar muito mais a edição de comandos
https://pliszko.com/blog/post/2021-10-31-natural-text-editin...
Não quero que a edição de texto no terminal se comporte de um jeito especial
Três key bindings do readline básicos que melhorariam a vida das pessoas se elas soubessem são Ctrl+W, Ctrl+O e Ctrl+R
Ctrl+W apaga a última palavra, como a Julia mencionou no texto
Ctrl+O executa a linha carregada do histórico e depois carrega novamente a próxima linha do histórico. Depois de ir para o primeiro comando no histórico, se você pressionar Ctrl+O cinco vezes, consegue executar cinco comandos em sequência
Ctrl+R pesquisa o histórico para trás enquanto você digita a string de busca. Pressionar Ctrl+R novamente vai para o resultado anterior; Ctrl+S avança no tempo. Execute com Enter ou Ctrl+O
Para usar essa combinação de teclas na busca para frente, você precisa saber por que ela não funciona e estar disposto a desativar o XOFF
No Windows Terminal, Ctrl+C sempre se comporta corretamente. Se houver texto selecionado, copia; se não houver, mata o processo em execução
Os apps de terminal no Linux não copiam com Ctrl+C e exigem Ctrl+Shift+C. Para colar, ao pressionar Ctrl+V entram caracteres estranhos, e como em todos os outros apps é Ctrl+V, a memória muscular faz a gente cair nessa toda hora
Fico me perguntando se existe algum app de terminal para Linux que se comporte como o Windows Terminal e não dê um tapa na mão por “uso inadequado de teletipo”
Esse método usa um buffer separado do Xorg, então mesmo que você copie algo com Ctrl+C e depois selecione outro texto, Ctrl+V cola o primeiro, enquanto o clique do meio cola o segundo
No Windows ou em smartphones, às vezes é irritante deixar algo apenas selecionado e esquecer de apertar algo como Ctrl+C
Acho que não há terminais Linux que façam como o Windows Terminal. Isso porque o programa em execução dentro do terminal pode querer tratar Ctrl+C diretamente. Um editor de texto é um exemplo, e o terminal nem sabe qual programa está em execução no momento. Controle de jobs é responsabilidade do shell
map ctrl+c copy_or_interrupthttps://sw.kovidgoyal.net/kitty/actions/#action-copy_or_inte...