Removendo caracteres não imprimíveis usando o POSIX sed

2

Arquivos criados com roff e outras ferramentas "antigas" (por exemplo, páginas man em muitos sistemas Unix) geram texto em negrito e sublinhado em terminais minimalistas usando truques envolvendo caracteres ASCII não imprimíveis como "meio retrocesso" ^H para obter texto em negrito e sublinhado, por exemplo:

b^Hbo^Hol^Hld^Hd and _^Hu_^Hn_^Hd_^He_^Hr_^Hl_^Hi_^Hn_^He_^Hd

Se eu quiser converter isso em texto simples legível em bold and underline (ignorando a formatação), eu posso facilmente conseguir isso em vim usando algo como :%s:\(.\)\b::ge | %s:_\b\(.\)::ge .

Eu também posso enviar o texto através de tr -dc e usar um pouco da magia regex do perl para procurar palavras que são construídas inteiramente de pares de caracteres repetidos.

No entanto, isso parece algo que o sed deve ser capaz de manipular, o que tornaria muito mais limpo o uso em scripts.

Question: Is it possible to do this translation only using POSIX sed, i.e. without using GNU or BSD extensions?

O que está me causando problemas aqui é apenas o caractere não imprimível ^H (ASCII # 8). Há um truque mencionado na Sed - An Introduction de Bruce Barnett, mas de alguma forma eu não consegui fazer isso funcionar .

    
por Simon G. 17.07.2014 / 04:44

1 resposta

3

Você pode fazer isso somente usando POSIX sed ? Sim:

sed -e 's/.^H//g' < data

onde ^H é apenas um caractere de retrocesso literal. POSIX sed usa expressões regulares básicas POSIX , que são definidas sobre bytes - caracteres de impressão ou não, eles não se importam, então isso se comporta da mesma forma como se ^H fosse uma carta. Não há extensões envolvidas aqui. Observe que tudo o que você realmente quer fazer é remover os caracteres que foram retrocedidos, para que os grupos de captura em seu exemplo não sejam realmente necessários.

Você pode digitar o caractere de backspace na maioria dos casos com Ctrl + V Ctrl + H .

Eu acho que a pergunta latente que você tem é "como eu faço isso em um shell script?", onde um caractere de backspace literal pode ser desagradável para trabalhar (embora vim aceite muito bem o mesmo Ctrl + V Ctrl + H para escrever um em). É aqui que a introdução que você vinculou usa tr .

POSIX tr suporta vários escape caracteres , incluindo o escape \b simbólico para um caractere de retrocesso. Você pode salvar um caractere de retrocesso em uma variável e substituí-la na expressão sed acima:

BACKSPACE=$(echo x | tr 'x' '\b')
sed -e "s/.$BACKSPACE//g" < data

Acabamos de dizer a tr para substituir um x pelo caractere de retrocesso e dar a ele um único x como entrada. Isso funciona bem em todos os sistemas que eu tenho acesso, incluindo o Solaris. No entanto, printf é também uma ferramenta definida por POSIX , e suporta os mesmos escapes:

BACKSPACE=$(printf '\b')
sed -e "s/.$BACKSPACE//g" < data

Isso é mais simples e direto que a versão tr . Observe a citação dupla em torno da expressão sed , para que não seja mais suprimida a interpolação de variáveis. Você também pode usar substituição de comandos para colocar o printf '\b' in diretamente se você ' só vai usá-lo uma vez, em vez de usar uma variável.

Podemos verificar se isso funciona com hexdump (ou hd ):

$ dash
$ hexdump -C data
00000000  62 08 62 6f 08 6f 6c 08  6c 64 08 64 0a           |b.bo.ol.ld.d.|
$ BACKSPACE=$(printf '\b')
$ sed -e "s/.$BACKSPACE//g" < data | hexdump -C
00000000  62 6f 6c 64 0a                                    |bold.|

Conforme desejado, o caractere de retrocesso e o caractere precedente apagado são removidos da saída ( 0a é a nova linha de finalização).

    
por 17.07.2014 / 06:13