![如何将空格分隔的单词列表折叠成适合终端宽度的表格列](https://linux22.com/image/98940/%E5%A6%82%E4%BD%95%E5%B0%86%E7%A9%BA%E6%A0%BC%E5%88%86%E9%9A%94%E7%9A%84%E5%8D%95%E8%AF%8D%E5%88%97%E8%A1%A8%E6%8A%98%E5%8F%A0%E6%88%90%E9%80%82%E5%90%88%E7%BB%88%E7%AB%AF%E5%AE%BD%E5%BA%A6%E7%9A%84%E8%A1%A8%E6%A0%BC%E5%88%97.png)
我想以表格格式输出一系列以空格分隔的单词,逐行填充,以便没有任何内容超过终端的宽度,但可以最佳地使用可用空间,如下所示:
+-----------------------------------------------+
|polite babies embarrass rightful |
|aspiring scandalous mut disgusted |
|bell deeply writer jumbled |
|respired craggy |
(该框说明了终端的宽度 - 它不是输出的一部分)
fold
浮现在脑海中的命令是column
这样的:
$ fold words -s -w $COLUMNS | column -t
这几乎可以工作,但输出最终比(终端宽度)更宽,$COLUMNS
因为它首先在该宽度内折叠,然后拉伸空白以使它们对齐。
我需要的是两者合二为一的效果。是否有任何命令行工具(或 shell 内置工具)可以执行此操作?
答案1
看起来您需要获取所有可能的列数(从 2 到 COLUMNS/2)的总宽度,以确定每列的宽度以及可以容纳的最大列数。
和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
}
例子:
$ 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
对于非 ASCII 文本,请参阅获取字符串的显示宽度确定字符串的显示宽度。就像是:
#! /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
}
答案2
要生成等距的列,您可以使用 BSDrs
(也移植到 Debian 和衍生品(至少)并作为软件包提供)或 BSD column
(在bsdmainutils
Debian 上的软件包中):
tr -s '[:space:]' '[ *]' | rs -w "$COLUMNS"
tr -s '[:space:]' '[\n*]' | column -xc "$COLUMNS"
示例(垂直线用于显示 60 列宽屏幕的边缘,它不是输出的一部分):
$ 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 ┃
使用rs
,您可以添加选项-z
来减少列之间的空间,但这不会相应地优化列数。例如,在上面,它给出(与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 ┃
代替:
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 ┃
它也不适用于多字节字符或 0 宽度或双宽度字符。
默认情况下,列之间至少留有 2 个空格。您可以使用 将其更改为 1 -g 1
。