Como faço para criar colunas uniformes a partir de texto contendo guias e espaços?

3

Na forma mais simples, digamos que eu tenha um arquivo gerado automaticamente chamado file.txt . O conteúdo de file.txt aparece da seguinte forma:

 Source                    Destination                Maximum To  Maximum From Average Total   Average To           Average From
(192.168.1.1)   (192.168.1.2)       202.89 Kbps    0 bps         645 bps 645 bps 0 bps

Eu tentei várias variações do comando column , tudo sem sucesso. Como posso fazer com que esta saída seja assim:

Source         Destination     Maximum To     Maximum From     Average Total     Average To     Average From
(192.168.1.1)  (192.168.1.2)   202.89 Kbps    0 bps            645 bps           645 bps        0 bps

Eu sinto que deveria saber como fazer isso, mas estou esboçando um espaço em branco no momento e não encontrei nada até agora que esteja funcionando como esperado.

EDITAR: O comentário e a resposta abaixo usando sed funcionam muito bem para o meu exemplo original (e eu realmente prefiro a solução no comentário porque é mais simples e não envolve canalizar para tr ). Dito isto, as duas soluções executam exatamente o mesmo em um arquivo com várias linhas. O file.txt real incluirá centenas de linhas de endereços IPv4 de vários comprimentos. Ambas as soluções sed até agora retornaram o seguinte resultado no original (e mais preciso) file.txt :

Arquivo original.txt:

Source                    Destination                Maximum To  Maximum From Average Total   Average To           Average From
(10.10.10.21)     (192.168.123.122)      18.90 Kbps     0 bps         131 bps 131 bps 0 bps
(10.10.10.22)     (192.168.123.122)       10.88 Kbps     0 bps         23 bps 23 bps 0 bps
(10.10.10.23)     (192.168.123.123)       10.88 Kbps     0 bps         23 bps 23 bps 0 bps
(192.168.123.123) (192.52.168.123)       0 bps          22.84 Kbps    1.17 Kbps 0 bps     1.17 Kbps
(192.168.123.124)  (192.52.168.123)       0 bps          10.87 Kbps    19 bps 0 bps  19 bps

Arquivo.txt atualizado (depois de usar as duas soluções sugeridas até o momento):

Source                              Destination        Maximum To  Maximum From  Average Total  Average To  Average From
(10.10.10.21)                       (192.168.123.122)  18.90 Kbps  0 bps         131 bps        131 bps     0 bps
(10.10.10.22)                       (192.168.123.122)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(10.10.10.23)                       (192.168.123.123)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(192.168.123.123) (192.52.168.123)  0 bps              22.84 Kbps  1.17 Kbps     0 bps          1.17 Kbps
(192.168.123.124)                   (192.52.168.123)   0 bps       10.87 Kbps    19 bps         0 bps       19 bps

Existe uma solução atualizada que seja responsável por essa limitação de offset?

    
por rubynorails 06.10.2015 / 21:21

3 respostas

3

O script é baseado nos dados de amostra do OP.

sed '
    s/\s\s\+/:/g
    s/\([a-z)]\)\s\([(0-9A]\)/:/g
    ' file.txt | 
column -s: -t
  • primeiro altere o separador fácil encontrado (2 ou mais \s paces) por :
  • segundo, encontrar os separadores possíveis restantes:
    • entre letra baixa e dígito
    • após )
    • antes de A
  • format string com o separador da coluna :
por 08.10.2015 / 08:40
3

O seguinte script perl converte a entrada em campos separados por tabulações, contando com o conhecimento de que os dois primeiros campos têm apenas uma "palavra" cada e os campos restantes têm duas "palavras" cada. A saída disso é então canalizada para column -s $'\t' -t

É um método muito desajeitado e de força bruta, mas funciona.

#! /usr/bin/perl 

use strict;

while(<>) {
    my (@F, @fields, $i);

    @F=split;
    $fields[0] = $F[0] ;
    $fields[1] = $F[1] ;
    for $i (0..4) {
      $fields[$i + 2] = $F[$i*2 + 2] . ' ' . $F[$i*2 + 3];
    }

    print join("\t",@fields),"\n";
}

É usado assim:

$ ./bandwidth.pl bandwidth.txt | column -s $'\t' -t 
Source             Destination        Maximum To  Maximum From  Average Total  Average To  Average From
(10.10.10.21)      (192.168.123.122)  18.90 Kbps  0 bps         131 bps        131 bps     0 bps
(10.10.10.22)      (192.168.123.122)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(10.10.10.23)      (192.168.123.123)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(192.168.123.123)  (192.52.168.123)   0 bps       22.84 Kbps    1.17 Kbps      0 bps       1.17 Kbps
(192.168.123.124)  (192.52.168.123)   0 bps       10.87 Kbps    19 bps         0 bps       19 bps

BTW, este é um bom exemplo de por que nunca é uma boa ideia usar um delimitador (por exemplo, espaço) que também esteja nos campos delimitados. Isso apenas torna as coisas mais difíceis do que precisam ser ... e não há uma maneira confiável de distinguir entre delimitadores e conteúdos de campo que não requeiram conhecimento prévio do conteúdo e da estrutura do arquivo.

    
por 06.10.2015 / 23:14
0

Atualizado: Usando uma cópia do original completo, nomeando-o neste exemplo full_original.txt :

$ sed 's/\((\)/ /g;s/\(Average\)/ /g;s/ \([0-9]\)/  /g;s/\(\S\) \(\S\)/_/g' full_original.txt | column -t | tr _ ' '
Source             Destination        Maximum To  Maximum From  Average Total  Average To  Average From
(10.10.10.21)      (192.168.123.122)  18.90 Kbps  0 bps         131 bps        131 bps     0 bps
(10.10.10.22)      (192.168.123.122)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(10.10.10.23)      (192.168.123.123)  10.88 Kbps  0 bps         23 bps         23 bps      0 bps
(192.168.123.123)  (192.52.168.123)   0 bps       22.84 Kbps    1.17 Kbps      0 bps       1.17 Kbps
(192.168.123.124)  (192.52.168.123)   0 bps       10.87 Kbps    19 bps         0 bps       19 bps

Explicação

A maioria desta solução usa uma abordagem de "dividir e conquistar", na qual você tem vários problemas separados, endereçando-os individualmente. Em seguida, ele é montado no final por meio do comando magic column , com um toque final de tr :

  • padrão básico é s/searchstring/replacestring/g' , g para ganancioso / global, portanto, aplica-se a todas as correspondências e não apenas à primeira
  • usamos o agrupamento, por isso \(somegroup\) na parte de pesquisa pode ser reimpressa via se for o primeiro grupo, se for o segundo grupo, etc.
  • ponto-e-vírgula ; nos permite colocar vários comandos de pesquisa e substituição em uma única sed instância, mais eficiente que a tubulação e, portanto, executar vários seds como sed command | sed command | sed command .... etc
  • s/\((\)/ /g lida com ...123) (19... empurrando os dois valores de IP entre parênteses mais do que um único espaço um do outro para evitar um problema de deslocamento que o OP descobriu. Ele faz isso combinando qualquer parêntese de abertura ( e prefixando com um espaço, de modo que ele se torne space + (
  • s/\(Average\)/ /g é a parte que lida com a forma como Maximum From Average Total não tem separação especial, dificultando uma busca e substituição posteriores, então, inicialmente, adicionamos um espaço adicional antes de cada ocorrência de Average
  • s/ \([0-9]\)/ /g para separar os valores do campo no texto original 645 bps 645 bps 0 bps , prefixando qualquer ocorrência de space+number com um espaço, para que se torne space+space+number , novamente para ajudar um comando sed posterior a diferenciá-los
  • o último comando sed s/\(\S\) \(\S\)/_/g é uma solução alternativa, ele procura non-space+space+non-space e, com o agrupamento, altera para que o espaço seja transformado em um sublinhado. Isso mantém Maximum To juntos para o último comando column que usamos, então ele se torna Maximum_To
  • | column -t canaliza para o comando de coluna que, por padrão, man column diz: By default, the column command will merge multiple adjacent delimiters into a single delimiter when using the -t option para manipular o espaço variável entre os textos como um único delimitador.
  • A coluna
  • também realiza a reformatação para alinhar os textos
  • finalmente | tr _ ' ' desfaz a solução alternativa de converter espaços em sublinhados ( _ ) usando o comando tr , converte todos _ , de volta para o espaço ' ' .

E assim você tem a saída que deseja.

    
por 06.10.2015 / 22:07