Por que a saída de “column” está desalinhada com uma entrada colorida ANSI?

3

Estou trabalhando em uma linha de comando que recupera alguns dados ( curl ), extrai os campos relevantes ( awk ) e formata-os ( coluna ).

Funciona bem, embora seja muito feio (mas todo o meu script começa com um "muito longo e feio" one-liner) mas quando tento fazer com que alguma coluna de cores dê errado.

Esta é a versão simples (reduzida) que funciona :

curl "http://webservices.rm.ingv.it/fdsnws/event/1/query?lat=42.35&lon=13.4&maxradius=5.0&starttime=2016-01-01T00:00:00&endtime=2016-12-31T23:59:59&minmag=5&format=text&orderby=time-asc" 2>/dev/null  \
 | awk 'BEGIN { FS= "|"; OFS= "|" }  {print  $1, $2, $5, $10, $11, $13}' \
 | column -t -s '|'

Agora, quero sublinhar alguns campos e adicionar alguns código de escape ANSI no awk:

curl "http://webservices.rm.ingv.it/fdsnws/event/1/query?lat=42.35&lon=13.4&maxradius=5.0&starttime=2016-01-01T00:00:00&endtime=2016-12-31T23:59:59&minmag=5&format=text&orderby=time-asc" 2>/dev/null  \
 | awk 'BEGIN { FS= "|" ; OFS= "|" }  \
     $13~/Rieti/||/Perugia/ {$13="3[1;31m"$13"3[0m"} \
     $11~/[0-9]+/ && $11 > 5.8 {$11="3[1;33m"$11"3[0m"} 
     {print  $1, $2, $5, $10, $11, $13 }' \
 | column -t -s '|'

Agora, o alinhamento está errado (veja a foto).

Porquê?Ecomopossoconsertarisso?

UPDATE

Eujáviapergunta Problema com comando de coluna e códigos de escape de cor mas não resolve meu problema porque as respostas dele são aplicadas e trabalham no caso de uma linha completamente colorida.

No meu caso, não posso aplicar ou adaptar as respostas (ou não consigo) porque:

  1. O problema é circunscrito ao caso em que a coluna $ 11 é colorida, independentemente da coluna subseqüente.
  2. Não consigo ver uma maneira boa ou elegante de adicionar o código de cores após coluna.
    Se eu enviar a saída da coluna para awk para o teste eu não sei como instruir o awk para separar os campos corretamente (se os campos fossem separados por mais espaço eu poderia usar um regex mas em alguns casos a separação é por um único espaço, e o awk não saberia reconhecer espaços entre palavras e espaços como Separadores de campos).

A única coisa que posso ver é que, se eu mover o código de cor de redefinição da atribuição para o bloco de impressão, a linha primeiro era melhor espaçada, como a versão de saída simples (veja abaixo,3[0m sublinhado na segunda linha de comando):

Então, como podemos consertar isso? Existe outra maneira, mais elegante, de colorir como eu fiz?

(Eu sei, eu posso fazer melhor com algumas linhas de perl, mas estou curioso sobre esse problema)

    
por Antonio 25.01.2017 / 17:02

2 respostas

0

Pode haver três maneiras simples de corrigir isso:

  1. sempre use sequências de escape nessas colunas para manter o mesmo comprimento
  2. coloque os escapes em suas próprias colunas (4 colunas extras), embora isso adicione espaço em branco extra na saída
  3. formata depois de column , como você sugeriu

Algumas outras considerações podem ser encontradas aqui: Uma ferramenta de shell para" tablify "dados de entrada contendo códigos de escape ANSI .

Para a primeira opção, em vez de usar apenas 3[1;31m para vermelho use 3[31;1m para vermelho e 3[31;0m para "não vermelho" ou simples - o código 0 desfaz qualquer código anterior, mesmo aqueles na mesma sequência . Então todas as colunas têm o mesmo tamanho de códigos de escape.

BEGIN { FS=OFS="|" }  
function colour(ss,cc)    { return "3[" cc ";1m" ss "3[0m"; }
function notcolour(ss,cc) { return "3[" cc ";0m" ss "3[0m"; }
{  
  if ($13~/(RI|PG)/)    { $13=colour($13,31)    } 
                   else { $13=notcolour($13,31) }
  if (($11+0) > 5.8)    { $11=colour($11,33)    }
                   else { $11=notcolour($11,33) }
  print $1, $2, $5, $10, $11, $13 
}

(Há uma série de pequenas simplificações e correções aplicadas no acima também, incluindo uma para coincidir com alterações nos dados de origem.)

O problema com essa abordagem é que ela depende de seu column e libc . (Meu column do util-linux-2.23.2) não verifica o código de retorno de wcswidth() , que é -1 quando são encontradas não imprimíveis, em vez da largura real; isso realmente atrapalha a formatação da tabela. A última versão do util-linux-2.30.1 usa um novo < em> libsmartcols que resolve este problema, mas faz isso substituindo as não-imprimíveis por uma versão \x hex-codificada - então você perde as fugas brutas: / Que você pode consertar com a deselegante :

curl ... | awk ... | column -t -s '|' | while read -r line; do printf "$line\n"; done

em que printf interpreta as fugas. Você poderia substituir 3 por \x1b em seu próprio código para o mesmo efeito. Eu não tenho certeza se você está usando Linux.)

Para a terceira opção, você precisará de um column que suporte -o para definir o separador de saída, o padrão é dois espaços. Defina como " | ", então você pode usar isto:

curl ... | column -t -s "|" -o "|" | awk '
BEGIN { FS="|" }  
function colour(ss,cc) { return sprintf("3[%i;1m%s3[0m",cc,ss) }
{  
  if ($13~/(RI|PG)/) { $13=colour($13,31) } 
  if (($11+0) > 5.8) { $11=colour($11,33) }
  print $1, $2, $5, $10, $11, $13 
}'

O truque aqui é usar column com entrada delimitada por pipe e saída, ele corrige as larguras e podemos seguramente processar isso com awk , preservando todos os espaços importantes. Se o seu column não suporta -o , você pode fingir com:

curl ... | sed -e 's/|/^|/g' | column -t -s^ | awk ...

Isso duplica o separador para " ^| ", column usa ^ e o awk usa | . Isso faz com que a suposição de que ^ não apareça nos dados é claro. Uma guia difícil pode funcionar em vez disso.

Eu acho que você sabe o "porquê" agora, mas para ser claro:

  • column pode contar octetos (ou caracteres) ingenuamente com strlen() / wcslen() , isso não corresponderá ao comprimento processado pelo terminal
  • column pode contar o comprimento usando isprint() , também incorreto com escapes de terminal
  • column pode desistir (como o meu) em qualquer coluna quando não imprimíveis forem encontrados

Embora a remoção de sequências de código de cores seja um problema razoavelmente direto, não há uma maneira robusta de contornar isso sem ter um pedaço do emulador de terminal ANSI dentro de column .

    
por 17.08.2017 / 20:12
0

Este snippet, modificado do OP:

# Utility functions: print-as-echo, print-line-with-visual-space.
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }

pl " Results, highlight:"
# Original code from post:
# curl "http://webservices.rm.ingv.it/fdsnws/event/1/query?lat=42.35&lon=13.4&maxradius=5.0&starttime=2016-01-01T00:00:00&endtime=2016-12-31T23:59:59&minmag=5&format=text&orderby=time-asc" 2>/dev/null  \
#  | awk 'BEGIN { FS= "|"; OFS= "|" }  {print  $1, $2, $5, $10, $11, $13}' \
#   | column -t -s '|'

# Codes my-highlight, my-hilite:
# https://unix.stackexchange.com/questions/46562/how-do-you-colorize-only-some-keywords-for-a-bash-script

SITE="http://webservices.rm.ingv.it/fdsnws/event/1/query?lat=42.35&lon=13.4&maxradius=5.0&starttime=2016-01-01T00:00:00&endtime=2016-12-31T23:59:59&minmag=5&format=text&orderby=time-asc"

curl "$SITE" > data1

awk 'BEGIN { FS= "|"; OFS= "|" }  {print  $1, $5, $10, $11, $13}' data1 |
tee f1 |
column -t -s '|' |
my-highlight -r "Norcia"

pl " Results, hilite:"
awk 'BEGIN { FS= "|"; OFS= "|" }  {print  $1, $5, $10, $11, $13}' data1 |
tee f2 |
column -t -s '|' |
my-hilite -f blue "Norcia"

produz:

-----
 Results, highlight:
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   788    0   788    0     0   2566      0 --:--:-- --:--:-- --:--:--  2575
#EventID  Depth/Km  MagType  Magnitude  EventLocationName
7073641   8.1       Mw       6.0        1 km W Accumoli (RI)
7076161   8.0       Mw       5.3        5 km E Norcia (PG)
8663031   8.7       Mw       5.4        3 km SW Castelsantangelo sul Nera (MC)
8669321   7.5       Mw       5.9        3 km NW Castelsantangelo sul Nera (MC)
8788671   481.4     ML       5.8        Tirreno Meridionale (MARE)
8863681   9.2       Mw       6.5        5 km NE Norcia (PG)

-----
 Results, hilite:
#EventID  Depth/Km  MagType  Magnitude  EventLocationName
7073641   8.1       Mw       6.0        1 km W Accumoli (RI)
7076161   8.0       Mw       5.3        5 km E Norcia (PG)
8663031   8.7       Mw       5.4        3 km SW Castelsantangelo sul Nera (MC)
8669321   7.5       Mw       5.9        3 km NW Castelsantangelo sul Nera (MC)
8788671   481.4     ML       5.8        Tirreno Meridionale (MARE)
8863681   9.2       Mw       6.5        5 km NE Norcia (PG)

Isso usa o método 3 de mr.spuratic, colorize após columnize.

Eu eliminei um campo para facilitar a leitura, depois apliquei 2 scripts (renomeados com um prefixo my - aqui) do thread Como você colorir apenas algumas palavras-chave para um script bash? - ambos trabalharam colorindo a corda Norcia depois de executar a coluna. (Se alguém puder me indicar métodos para mostrar cores em posts, eu ficaria grato.)

Presumi que essas cadeias não apareceriam em outro lugar na saída, portanto, campos específicos não são um problema, a linha inteira é examinada para uma correspondência. Se essa suposição não for o caso, então esta solução é de pouco valor, além de chamar a atenção para os scripts hilite e destaque .

Isso foi feito em um sistema como:

OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.9 (jessie) 
bash GNU bash 4.3.30

Se você usar o script highlight , precisará do spc (no pacote Debian supercat ); aqui estão alguns detalhes sobre isso:

spc     colorize and print to standard output (man)
Path    : /usr/bin/spc
Package : supercat
Home    : http://supercat.nosredna.net/
Version : 2008
Type    : ELF64-bitLSBexecutable,x86-64,version1(SYSV ...)
Help    : probably available with -h,--help

Felicidades ... felicidades, drl

    
por 26.10.2017 / 14:32