Por que o separador de unidade (ASCII 31) é invisível na saída do terminal?

16

O caractere ASCII do separador de unidade (ASCII 31, octal 37) é visível no Vim como ^_ . Mas se eu imprimir o mesmo arquivo no terminal, o caractere ficará invisível. Isso faz com que os campos em uma linha fiquem presos juntos:

# In Vim and less:

first field^_second field^_last field

# cat the same file to terminal:
cat delim.txt
first fieldsecond fieldlast field

# print 2nd field with awk 
cat delim.txt | awk 'BEGIN {FS = "7"} {print $2}'
second field

Suponho que posso tornar o separador de unidade visível com cat -v:

cat -v delim.txt
first field^_second field^_last field

Mas isso é um pouco complicado. Por que o separador de unidade não tem uma representação visível quando impresso em stdout no shell Bash? Eu não posso nem copiar e colar a saída do shell corretamente; o separador de unidade se perde no processo.

    
por dan 05.05.2014 / 22:41

3 respostas

18

O caractere separador de unidade ( US ), também conhecido como IS1 , está na classe de caractere cntrl e não é não na classe de caractere print . É um caractere de controle que se destina a organizar o texto em grupos, para programas projetados para usar essas informações . Em geral, os caracteres não imprimíveis provavelmente serão interpretados e renderizados de forma diferente em diferentes programas ou ambientes.

O motivo pelo qual você está vendo isso representado como ^_ no Vim é porque o Vim é um editor interativo. Ele pode renderizar caracteres não imprimíveis livremente, desde que o caractere binário correto seja gravado no disco.

Não é possível obter o mesmo comportamento no shell, pois os programas shell do Unix são gravados para operar e transmitem texto simples entre si. Quando você usa cat de um arquivo, o texto que é gravado no terminal deve ser o que está realmente no arquivo.

Isso deixa o dispositivo terminal para interpretar o caractere. E acontece que alguns emuladores de terminal fazem renderizar o caracter de US de forma diferente dos outros. Em gnome-terminal (ou qualquer terminal vte -based), o caractere será renderizado como uma caixa contendo o código hexadecimal 001F . Em xterm ou rxvt , o personagem é realmente invisível.

    
por 05.05.2014 / 23:22
10

O separador de unidade está na faixa ASCII de caracteres de controle e, portanto, não o faz (ou normalmente não deve ) tem uma representação visual.

O Vim e alguns outros editores os exibem, para que você possa editá-los. Como você percebeu, cat -v também é exibido. A página man mostra que -v é a forma abreviada de --show-nonprinting , o que faz com que ela substitua os caracteres não imprimíveis por uma representação imprimível, que não é o conteúdo original do arquivo e pode causar problemas, se o saída é na verdade para outro programa.

A representação que você vê já sugere que é um caractere de controle: um caractere com o prefixo ^ é uma notação comum para Ctrl + o caractere, que é a combinação de teclas que produz esse caractere terminal. Ctrl + _ permite que você insira o separador de unidade no vim, por exemplo. Mas outro editor ou algum visualizador de GUI pode exibir o código hexadecimal, um espaço reservado ou algo completamente diferente.

Como o seu terminal não imprime os caracteres de controle, ele também não é copiado ao selecionar o texto (os caracteres de espaço em branco, como nova linha e guia, são uma exceção aqui, que também são caracteres de controle). Outro exemplo de caracteres de controle no terminal que são geralmente ignorados durante a cópia são os códigos de cores, que são um caractere ESC seguido pelo código para colorir o texto.

Portanto, para mostrar os caracteres no seu terminal, não há outra maneira senão usar um programa que substitua o separador de unidade por algum caractere imprimível.

    
por 06.05.2014 / 00:05
3

Um pouco na margem das outras respostas (muito boas), se você quiser alterar somente o caractere de controle ^_ ao exibir o conteúdo do arquivo, você pode querer transliterar usando o utilitário tr (e um pouco de sintaxe compatível com o bash):

# Replace the control character US (^_) by *one* other character
$ cat my.file | tr $'\c_' ':'

Se você precisar substituir esse caractere de controle pelo formulário "expandido", precisará de sed :

# Replace the control character US (^_) by any string
cat /tmp/f | sed s/$'\c_'/^_/g

Por favor, note a sintaxe $'\cX' : esta sintaxe informar o seu (shell compatível com bash) para substituir o caractere de controle correspondente. Consulte wikipedia para obter uma lista de alias de caracteres de controle usando a "notação de circunflexão". Se você não gostar dessa sintaxe, poderá preferir usar a notação octal $'7' ou hexadecimal $'\x31' .

    
por 06.05.2014 / 11:39