Como exibir caracteres de controle (^ C, ^ D, ^ [,…) de maneira diferente no shell

13

Quando você digita caracteres de controle no shell, eles são exibidos usando o que é chamado de "notação de intercalação". Escape, por exemplo, é escrito como ^[na notação de sinal de intercalação.

Eu gosto de personalizar meu shell bash para torná-lo legal. Eu, por exemplo, mudei o meu PS1e fiquei PS2colorido. Agora, quero que os caracteres de controle tenham uma aparência única e os tornem mais distinguíveis dos caracteres comuns.

$ # Here I type CTRL-C to abort the command.
$ blahblah^C
          ^^ I want these two characters to be displayed differently

Existe uma maneira de fazer meu shell destacar caracteres de controle de maneira diferente?

É possível fazê-lo exibi-los em uma fonte em negrito ou talvez fazê-los aparecer em cores diferentes do texto normal?

Estou usando o shell bash aqui, mas não marquei a pergunta bashporque talvez exista uma solução que se aplique a muitos shells diferentes.

Observe que não sei em que nível o destaque dos caracteres de controle ocorre. Eu primeiro pensei que estava na própria concha. Agora ouvi dizer que é a linha de leitura que controla como os caracteres de controle estão em conchas como o bash . Portanto, a pergunta agora está marcada readlinee eu ainda estou procurando respostas.

wefwefa3
fonte
por que você acha que o shell está destacando-os? para bashele é readlinelidar com essas coisas, e para a maioria dos outros é o driver tty.
mikeserv
Eu não sabia Por isso escrevi a nota. Foi apenas o meu palpite, porque outros aplicativos gostam vie lessgeralmente destacam os caracteres de controle de maneira diferente do texto comum. Vou editar a pergunta com esta informação. Obrigado!
wefwefa3
isso faz sentido. esse tipo de coisa é mais difícil para um shell porque é uma segunda prioridade. com os editores, todo o trabalho deles é fornecer uma interface de arquivo de tela <>, mas o shell é um intérprete de comando para uma linguagem interpretada primeiro e um editor de linha em segundo (ou não) .
mikeserv
O Readline é usado por outros programas, mas também faz parte do bash: é uma biblioteca vinculada a ele. E as conchas de hoje, especialmente o bash, fazem praticamente tudo o que você pode imaginar.
Alexis
Basta usar o zshque faz isso fora da caixa.
Stéphane Chazelas

Respostas:

20

Quando você pressiona Ctrl+X, o emulador de terminal grava o byte 0x18 no lado principal do par pseudo-terminal.

O que acontece a seguir depende de como a disciplina tty line (um módulo de software no kernel que fica entre o lado mestre (sob controle do emulador) e o lado escravo (com o qual os aplicativos em execução no terminal interagem)) está configurada.

Um comando para configurar essa disciplina de linha tty é o sttycomando.

Ao executar um aplicativo idiota como catesse não está ciente e não se importa se seu stdin é um terminal ou não, o terminal está no modo canônico padrão , onde a disciplina de linha tty implementa um editor de linha bruta .

Alguns aplicativos interativos que precisam mais do que o editor de linha bruta geralmente alteram essas configurações na inicialização e as restauram ao sair. As conchas modernas, quando solicitadas, são exemplos dessas aplicações. Eles implementam seu próprio editor de linha mais avançado.

Normalmente, enquanto você digita uma linha de comando, o shell coloca a disciplina da linha tty nesse modo e, quando você pressiona enter para executar o comando atual, o shell restaura o modo tty normal (como estava em vigor antes de emitir o prompt).

Se você executar o stty -acomando, verá as configurações atuais em uso para os aplicativos mudos . É provável que você ver a icanon, echoe echoctldefinições a ser habilitado.

O que isso significa é que:

  • icanon: esse editor de linha bruta está ativado.
  • echo: os caracteres digitados (que o emulador de terminal grava no lado mestre) são repetidos (disponibilizados para leitura pelo emulador de terminal).
  • echoctl: em vez de serem repetidos como asis, os caracteres de controle são repetidos como ^X.

Então, digamos que você digite A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Return.

Seu emulador de terminal irá enviar: AB\bC\x18\b\r. A disciplina de linha ecoará de volta :,AB\b \bC^X\b \b\b \b\r\n e um aplicativo que lê a entrada do lado escravo ( /dev/pts/x) lerá AC\n.

Tudo o que o aplicativo vê é AC\n, e somente quando você pressiona, Enterpara que ele não possa ter nenhum controle na saída por ^Xlá.

Você notará que, para eco , o primeiro ^H( ^?com alguns terminais, consulte a eraseconfiguração) resultou no \b \benvio de volta ao terminal. Essa é a sequência para mover o cursor para trás, substituir o espaço, mover o cursor para trás novamente, enquanto o segundo ^Hresultou na \b \b\b \bexclusão desses dois ^e Xcaracteres.

O próprio ^X(0x18) estava sendo traduzido para ^e Xpara saída. Como B, não chegou ao aplicativo, pois o excluímos com Backspace.

\r(aka ^M) foi traduzido para \r\n( ^M^J) para eco e \n( ^J) para o aplicativo.

Então, quais são as nossas opções para essas aplicações idiotas :

  • desativar echo( stty -echo). Isso muda efetivamente a maneira como os caracteres de controle são repetidos, ... não repetindo nada. Não é realmente uma solução.
  • desativar echoctl. Isso muda os caracteres de controle caminho (exceto ^H, ^M... e todos os outros utilizados pelo editor de linha) são ecoados. Eles são ecoados como estão. Por exemplo, o caractere ESC é enviado como o byte \e( ^[/ 0x1b) (que é reconhecido como o início de uma sequência de escape pelo terminal), ^Gvocê envia um \a(um BEL, emitindo um bipe no terminal) ... Não é uma opção .
  • desative o editor de linha bruta ( stty -icanon). Não é realmente uma opção, pois os aplicativos brutos se tornariam muito menos utilizáveis.
  • edite o código do kernel para alterar o comportamento da disciplina de linha tty, para que o eco de um caractere de controle seja enviado em \e[7m^X\e[mvez de apenas ^X(aqui \e[7mgeralmente habilita o vídeo reverso na maioria dos terminais).

Uma opção poderia ser usar um invólucro como rlwrapesse, um hack sujo para adicionar um editor de linhas sofisticado a aplicativos burros. Com efeito, esse invólucro tenta substituir read()s simples do dispositivo terminal por chamadas para o editor de linha readline (que alteram o modo da disciplina de linha tty).

Indo ainda mais longe, você pode tentar soluções como esta, que sequestram toda a entrada do terminal para passar pelo editor de linha do zsh (que por acaso destaca ^Xs no vídeo reverso), contando com o :execrecurso da tela GNU .

Agora, para aplicativos que implementam seu próprio editor de linha, cabe a eles decidir como o eco é feito. bashusa readline para aquilo que não tem suporte para personalizar como os caracteres de controle são repetidos.

Para zsh, veja:

info --index-search='highlighting, special characters' zsh

zshdestaca caracteres não imprimíveis por padrão. Você pode personalizar o destaque com, por exemplo:

zle_highlight=(special:fg=white,bg=red)

Para branco e vermelho, destaque para esses caracteres especiais.

A representação de texto desses caracteres não é personalizável.

Em um locale UTF-8, 0x18 será processado como ^X, \u378, \U7fffffff(dois pontos de código unicode não atribuídos), como <0378>, <7FFFFFFF>, \u200b(um caractere Unicode não-realmente impressa) como <200B>.

\x80em um código de idioma iso8859-1 seria renderizado como ^�... etc.

Stéphane Chazelas
fonte
3

Geralmente, tenho esse código no meu arquivo .bashrc:

function get_exit_status()
{
        local code=$?
        if [ $code -ne 0 ]
        then
                printf $'\001\033[31m\002'"($code)"$'\001\033[0m\002'" "
        fi
}

e então eu chamo essa função no meu PS1

PS1='\u@\h \w $(get_exit_status)'

Com isso, se você pressionar ^ C, verá no seu prompt

I@mycomputer ~ ^C
I@mycomputer ~ (130)

Todos os códigos de status de saída que não são "0" serão solicitados.

toed
fonte
$? se expande para sair do status no PS1, por exemplo, PS1 = '$? \ u @ \ h \ w'
teknopaul