Existe um gato seguro?

Existe um gato seguro?

De vez em quando acontece que eu cato um binário do curl ou do sistema de arquivos local. Na maioria dos casos, o terminal quebrado pode ser consertado comreiniciar. Em outros casos, principalmente se o binário for grande, o terminal ficará travado por vários minutos, imprimindo uma saída como esta:

2c1

também conhecido como

c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;

Tenho três perguntas em relação a este cenário;

  1. O que significa 2c1 e por que o terminal está imprimindo isso?
  2. Você já viu alguém catse protegendo contra esse comportamento indesejado em uma sessão interativa?
  3. Você tem alguma sugestão sobre como programar tal gato (em cee ou golang)?

Meu instinto inicial foi envolver cat em uma função para detectar isso, mas logo percebi que é bastante difícil acertar e teria vários casos extremos.

function cat() {
    # warn user if
    #   - argument 1 is a large  executable 
    #   - argument 1 to the previous command in the a pipe-chain looks like a large binary
    # abort if
    #   - session is interactive and we are able to detect 2c1 garbage
}

Uma solução prática poderia ser sempre usar menos (com LESSPIPE) ao analisar entradas "inseguras", mas esta questão não é sobre pagers. Estou ciente de cada vez menos tubo. Eu os uso ativamente todos os dias. Talvez menos+menospipeé a solução para este problema, que o(s) autor(es) de menos implementou há cerca de 20-30 anos enfrentando o mesmo problema.

No entanto, cat é diferente de um "pager" em mais de um aspecto... Principalmente cat não é interativo. Isso é significativo para mim.

A sugestão sobre less+lesspipe é genuinamente boa (imho) em termos práticos, mas estou mais preocupado com os detalhes dos caracteres de controle, sequências de escape especiais e como diferentes terminais lidam com essas entradas.

Estou mais interessado nos detalhes técnicos dos caracteres de controle e em como os terminais ou shells interpretam o "lixo" e os caracteres de controle. Não estou perguntando "como você resolveria esse problema". Estou perguntando "por que o terminal está lidando com arquivos binários assim".

Responder1

Eu usaria less, em vez disso, que avisa sobre arquivos binários e em alguns sistemas pode lidar com vários tipos (por exemplono CentOS 7, posso fazer less file.rpme ver os arquivos no RPM). Acredito que isso se chama "lesspipe".

Além disso, da próxima vez que isso acontecer, você pode tentar resetvoltar tput resetao normal. Eles enviam sequências de escape para o terminal que solicitam a redefinição para um estado lógico padrão e também fazem o equivalente a stty sanealterar as configurações do arquivo do dispositivo tty para um padrão sensato (o despejo de um arquivo binário não deve afetá-los). resettambém pode corrigir a noção do arquivo do dispositivo tty sobre o tamanho da janela do terminal ou emulador de terminal (conforme relatado por tty size) com os terminais que suportam consultá-lo.

Responder2

Ninguém mencionou 'strings'. Embora strings não sejam exatamente como cat, ele imprime apenas as strings de texto de dados de texto contínuos para torná-los mais seguros para visualização no terminal. Geralmente vem com o pacote binutils. É um programa útil para garantir rapidamente que você não obtenha nenhuma saída binária dos dados impressos e também é útil se você quiser apenas ver o não-binário contínuo. Observe que, por padrão, ele imprime apenas seções de texto contínuas com 4 ou mais caracteres ASCII. Isso pode ser ajustado com a opção -n.

Responder3

Você interage com um terminal ou emulador de terminal por meio de uma linha serial ou dispositivo pseudo-tty (que emula uma linha serial).

Embora exista um módulo de software no kernel que fica no meio como uma espécie de camada de adaptação e faz alguma transformação (discutida brevemente mais tarde), você normalmente:

  • envia um fluxo de bytes para o terminal através daquela linha serial que o terminal interpreta como glifos para renderizar em sua tela ou instruções especiais para alterar seu comportamento
  • em troca, o terminal envia um fluxo de bytes ao computador por meio de outro fio naquela linha serial para informar ao computador o que você digitou ou para responder a algumas das consultas de controle recebidas.

Por exemplo, um terminal pode ser configurado como ISO8859-1 (também conhecido como terminal latin1), o que significa que quando ele recebe 0x53 0x74 0xe9 0x70 0x68 0x61 0x6e 0x65, ele o interpreta como renderizando os S, t, é, p, h, a, n, eglifos no posição atual do cursor em sua tela. E inversamente, quando o usuário digita S, o terminal envia o byte 0x53.

Valores de bytes no intervalo de 0 a 0x1f são interpretados comoao controlepersonagens. Ou seja, eles não são representados como glifos, mas possuem um significado especial.

Por exemplo:

  • 0x7 (BEL) gera um alerta de áudio ou vídeo
  • 0x8 (BS) move o cursor para a esquerda
  • 0xa (LF) move o cursor para baixo
  • 0xd (CR) move o cursor para a primeira coluna da tela
  • 0x9 (TAB) move o cursor para a próxima tabulação

Existem apenas 32 caracteres de controle nesse intervalo e a maioria dos terminais tem muito mais recursos ou formas de controlá-los. Além disso, você pode enviar sequências de mais deumbyte para controlar seu terminal. Para a maioria dos terminais e para a maioria dessas sequências, o primeiro byte é 0x1b (ESC) seguido por um ou mais bytes.

Por exemplo, embora existam caracteres de controle para mover o cursor para a esquerda ou para baixo como visto acima, não há nenhum para movê-lo para a direita ou para cima (como originalmente, em tele-máquinas de escrever, a direita seria feita com "espaço", mas em Terminais CRT que apagam o que está sob o cursor, e você não usaria uma tele-máquina de escrever, pois isso provavelmente criaria um atolamento de papel), então sequências de escape tiveram que ser introduzidas para eles, na maioria dos terminais 0x1b 0x5b 0x43 e 0x1b 0x5b 0x41 respectivamente (aliás, essa também é a sequência de bytes que muitos terminais enviam ao pressionar Righte Uppara aqueles que possuem essas teclas).

Agora, entre as sequências de escape suportadas pelos terminais estão algumas que:

  • alterar a cor do texto ou do plano de fundo e outros atributos de renderização gráfica
  • altere o conjunto de caracteres. Por exemplo, não há nenhum caractere grego em latin1, e os terminais (desde os dias pré-Unicode e ainda hoje) suportam a mudança para um conjunto de caracteres diferente para exibir letras de outros idiomas ou caracteres de desenho de caixa.
  • definir a posição das paradas de tabulação
  • pode consultar informações do terminal, como posição do cursor, cor, título da janela, tamanho...
  • pode afetar como a entrada é processada. Por exemplo, alguns terminais suportam a entrada em um modo em que, ao pressionar Shift+, Apor exemplo, ele não envia um Acaractere 0x41 (ASCII), mas uma sequência de bytes que codifica informações sobre modificadores (shift, alt, ctrl...) e código-chave.
  • alguns emuladores de terminal X11 reconhecem sequências de escape para alterar a fonte, o tamanho da janela, exibir imagens JPEG, enviar o conteúdo da tela para uma impressora...

Em um arquivo de texto, você geralmente tem apenas bytes (ou sequências de bytes se for UTF-8 ou outros conjuntos de caracteres multibyte) representando caracteres gráficos. A únicaao controleos caracteres que você encontrará em arquivos de texto são NL (0xa, também conhecido como LF) e TAB (0x9).

Quando você faz isso cat file.txt, catapenas lê o conteúdo file.txte grava em seu stdout. Se stdout for um arquivo de dispositivo serial ou pseudo-tty ( /dev/ttyS0, /dev/pts/0por exemplo) que possui uma disciplina de linha de terminal inserida nele, como seria o caso se você executasse esse comando a partir de um shell interativo em um emulador de terminal, a disciplina de linha traduz esses NLs para CR+NL (embora NLNL possa ser traduzido apenas para CRNLNL), então o terminal ao receber CRNL moverá o cursor para o início e depois para baixo.

Portanto, o texto do conteúdo do arquivo será exibido na tela do terminal, desde que o texto do arquivo esteja codificado no conjunto de caracteres do terminal.

Agora, os bytes em arquivos executáveis ​​ou outros arquivos binários aleatórios não se destinam a representar caracteres, eles podem ter qualquer valor, incluindo aqueles no intervalo de 0 a 31, portanto, quando enviados para um terminal, o terminal fará o que foi dito e os interpretará como caracteres de controle, o que pode fazer com que ele faça qualquer coisa conforme listado acima e muito mais e torná-lo completamente inutilizável.

Para se proteger contra isso, primeiro você não envia esses arquivos para um terminal, pois isso não faria sentido, ou se você não sabe se um arquivo pode ser um arquivo de texto (ou um arquivo destinado a ser visualizado literalmente por um terminal com sequências de escapepretendidopara serem interpretados por um terminal) ou não, você pode usar uma ferramenta que remova os caracteres de controle (pelo menos todos aqueles, exceto TAB e NL) ou forneça-lhes uma representação gráfica visual.

É isso que as opções -ve -tsuportadas por muitas catimplementações fazem. Onde com -v, todos, exceto NL e TAB, são convertidos para alguma ^Xnotação para os bytes 0 a 31 e 0x7f, M-^Xpara os bytes 0x80 a 0x9f e 0xff e M-Xpara os bytes 0xa0 a 0xfe que são representações visuais comuns de caracteres não ASCII. E -tfaz isso apenas para TAB (alterado para ^I).

Ou você pode usar um pager como lessor vimque viewfaz isso por padrão (pelo menos contanto que você não use as opções -r/ -Rraw) e é um pouco mais inteligente porque não transforma caracteres não-ASCII que são pretende ter representações gráficas em sua localidade e deixar mais claro quais bytes foram transformados usando modos de coloração ou destaque.

Ou você pode usar ferramentas dedicadas à visualização de arquivos não textuais, como hexdump -Cou xxd.

Veja também o lcomando do sedqual faz algo semelhante cat -vtee é padrão (contrário ao cat -vte) de uma forma menos ambígua:

sed -n l < a-file

Responder4

  • Sim, jovem padawan.
$ cargo search bat 
bat = "0.23.0"            # A cat(1) clone with wings.
  • Mas mestre, o que é uma carga?
$ cargo --help |any install rust 
Rust's package manager
      --list                List installed commands
      --explain <CODE>      Run `rustc --explain CODE`
    install     Install a Rust binary. Default location is $HOME/.cargo/bin
    uninstall   Uninstall a Rust binary
$ cargo install bat
(...)

mestre:Isso acontece se você tentar batum binário.

$ bat `which bat`
[bat warning]: Binary content from file '/home/jaroslav/.cargo/bin/bat'
will not be printed to the terminal (but will be present if the output
of 'bat' is piped). You can use 'bat -A' to show the binary file contents.
  • padawan:Há alguma desvantagem em rebater, mestre?
  • mestre:Sim, pode ser lento em arquivos grandes, mas isso ocorre em parte porque destaca a sintaxe estruturada. Essas coisas podem ser desabilitadas, por exemplo --style=plain --color=never
  • padawan:E quanto aos caracteres estranhos que cat envia para o terminal, mestre?
  • mestre:Isso acontece porque os terminais aceitarão e interpretarão alegremente qualquer coisa que se pareça com um código de escape ANSI (comando interno do terminal) e tentarão fazer o que o comando diz, se esse comando for implementado. Para uma breve introdução, confira istolista de sequências de escape de cores ansi

Veja como reproduzir esse comportamento:

$ echo -ne "\u1B\u5B\u63" | xxd
00000000: 1b5b 63                                  .[c

$ echo -ne "\u1B\u5B\u63" 
^[[?1;2c

$ 1;2c

informação relacionada