Como uma lista de palavras delimitada por espaço pode ser dobrada em colunas tabulares que se encaixam na largura do terminal?

3

Eu gostaria de produzir uma série de palavras delimitadas por espaço em um formato tabular, preenchendo linha por linha para que nada exceda a largura do terminal, mas o espaço disponível é usado de maneira ideal, como este:

+-----------------------------------------------+
|polite      babies      embarrass   rightful   | 
|aspiring    scandalous  mut         disgusted  |
|bell        deeply      writer      jumbled    |
|respired    craggy                             |

(a caixa ilustra a largura do terminal - não faz parte da saída)

Os comandos que vêm à mente são fold e column em um pipeline assim:

$ fold words -s -w $COLUMNS | column -t

Isso quase funciona, mas a saída é maior que $COLUMNS (a largura do terminal) porque é dobrada primeiro dentro dessa largura e, em seguida, o espaço em branco é esticado para alinhá-los.

O que eu preciso é o efeito de ambos em um. Existe alguma ferramenta de linha de comando (ou built-ins de shell) que possa fazer isso?

    
por starfry 18.11.2016 / 13:55

2 respostas

3

Para produzir colunas igualmente espaçadas, você pode usar o BSD rs (também portado para Debian e derivados (pelo menos) e disponível como um pacote lá):

tr -s '[:space:]' '[ *]' | rs -w"$COLUMNS"

Exemplo (linha vertical não faz parte da saída):

$ lorem -w 30 | tr -s '[:space:]' '[ *]' | rs -w60
earum          aspernatur     ipsa           sed            ┃
quod           sit            esse           quisquam       ┃
animi          reprehenderit  porro          et             ┃
delectus       neque          esse           quia           ┃
pariatur       amet           iste           voluptatem     ┃
provident      praesentium    et             sint           ┃
quo            animi          doloribus      veritatis      ┃
iusto          alias                                        ┃

Você pode adicionar a opção -z para reduzir o espaço entre as colunas, mas isso não otimiza o número de colunas de acordo. Por exemplo, no acima, dá (com rs -zw60 ):

earum      aspernatur     ipsa       sed                    ┃
quod       sit            esse       quisquam               ┃
animi      reprehenderit  porro      et                     ┃
delectus   neque          esse       quia                   ┃
pariatur   amet           iste       voluptatem             ┃
provident  praesentium    et         sint                   ┃
quo        animi          doloribus  veritatis              ┃
iusto      alias                                            ┃

Em vez de:

earum      aspernatur   ipsa       sed    quod              ┃
sit        esse         quisquam   animi  reprehenderit     ┃
porro      et           delectus   neque  esse              ┃
quia       pariatur     amet       iste   voluptatem        ┃
provident  praesentium  et         sint   quo               ┃
animi      doloribus    veritatis  iusto  alias             ┃

Ele também não funciona com caracteres de múltiplos bytes ou caracteres de largura 0 ou largura dupla.

Por padrão, ele deixa pelo menos 2 espaços entre as colunas. Você pode alterá-lo para 1 com -g 1 .

    
por 18.11.2016 / 16:52
4

Parece que você precisa obter a largura total em todo o número possível de colunas (de 2 a COLUMNS / 2) para determinar a largura de cada coluna e qual é o número máximo de colunas em que isso pode ser adequado. / p>

com perl :

#! /usr/bin/perl
use List::Util qw(sum);

$/ = undef;
@word = <> =~ /\S+/g;
$max = $ENV{COLUMNS}/2;
for ($i = 0; $i <= $#word; $i++) {
  $l = length $word[$i];
  for ($c = 2; $c <= $max; $c++) {
    if ($l > $w[$c][$i%$c]) {
      $w[$c][$i%$c] = $l
    }
  }
}
for ($c = $max; $c > 1; $c--) {
  last if $c + sum(@{$w[$c]}) - 1 <= $ENV{COLUMNS}
}
if($c > 1) {
  @w = @{$w[$c]};
  for ($i = 0; $i <= $#word; $i++) {
    if (($i+1)%$c && $i < $#word) {
      printf "%-*s ", $w[$i%$c], $word[$i]
    } else {
      print "$word[$i]\n"
    }
  }
} else {
  print "$_\n" for @word
}

Exemplo:

$ lorem -w 50 | COLUMNS=60 that-script
minima   aut     veritatis laudantium qui      voluptatem
est      nostrum quis      enim       placeat  hic
voluptas ab      ratione   sit        hic      sit
pariatur et      provident voluptas   aut      odio
aut      vero    atque     voluptatem amet     voluptatem
ipsum    iusto   omnis     tenetur    ratione  ratione
illo     ea      odit      excepturi  quisquam aut
nobis    porro   incidunt  corrupti   maxime   ad
est      sunt

Para texto não ASCII, consulte Obtenha a largura de exibição de uma sequência de caracteres para determinar a largura de exibição de uma sequência. Algo como:

#! /usr/bin/perl
use Text::CharWidth qw(mbswidth);
use List::Util qw(sum);

$/ = undef;
@word = <> =~ /\S+/g;
$max = $ENV{COLUMNS}/2;
for ($i = 0; $i <= $#word; $i++) {
  $l = mbswidth $word[$i];
  for ($c = 2; $c <= $max; $c++) {
    if ($l > $w[$c][$i%$c]) {
      $w[$c][$i%$c] = $l
    }
  }
}
for ($c = $max; $c > 1; $c--) {
  last if $c + sum(@{$w[$c]}) - 1 <= $ENV{COLUMNS}
}
if($c > 1) {
  @w = @{$w[$c]};
  for ($i = 0; $i <= $#word; $i++) {
    if (($i+1)%$c && $i < $#word) {
      printf $word[$i] . " " x ($w[$i%$c]+1-mbswidth($word[$i]))
    } else {
      print "$word[$i]\n"
    }
  }
} else {
  print "$_\n" for @word
}
    
por 18.11.2016 / 15:19