为什么“column”的输出与 ANSI 彩色输入不一致?

为什么“column”的输出与 ANSI 彩色输入不一致?

我正在使用一个命令行来检索一些数据(卷曲),提取相关字段(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= "|" }  {print  $1, $2, $5, $10, $11, $13}' \
 | column -t -s '|'

现在,我想在一些字段下划线,然后添加一些ANSI 转义码在 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="\033[1;31m"$13"\033[0m"} \
     $11~/[0-9]+/ && $11 > 5.8 {$11="\033[1;33m"$11"\033[0m"} 
     {print  $1, $2, $5, $10, $11, $13 }' \
 | column -t -s '|'

现在,对齐是错误的(见图)。

在此输入图像描述

为什么?我该如何解决它?

更新

我已经看到这个问题了列命令和颜色转义代码的问题但并不能解决我的问题,因为他的答案适用于全彩色线的情况。

就我而言,我无法应用或调整答案(或者我不能),因为:

  1. 该问题仅限于列 $11 被着色的情况,无论后续列如何。
  2. 我看不出添加颜色代码的好方法或优雅方法柱子。
    如果我将列的输出发送到 awk 进行测试,我不知道如何指示 awk 正确分隔字段(如果字段被更多空间分隔,我可以使用正则表达式,但在某些情况下分隔是由单个空格, awk 不知道如何将单词之间的空格和空格识别为字段分隔符)。

我唯一能看到的是,如果我将重置颜色代码从分配移动到打印块第一的行的间距更好,就像纯输出版本一样(见下文,\033[0m第二个命令行中的下划线): 在此输入图像描述

那么,我们该如何解决呢?还有另一种更优雅的方式来像我一样着色吗?

(我知道,我可以用一些 perl 行做得更好,但我对这个问题很好奇)

答案1

可能有三种简单的方法可以解决此问题:

  1. 始终在这些列中使用转义序列来保持相等的长度
  2. 将转义符放在自己的列中(4 个额外列),尽管这会在输出中添加额外的空格
  3. column按照您的建议进行格式化

其他一些注意事项可以在这里找到:用于“表格化”包含 ANSI 转义码的输入数据的 shell 工具

对于第一个选项,不是仅使用\033[1;31m红色\033[31;1m,而是使用红色,以及\033[31;0m“非红色”或普通 - 0 代码会撤消任何前面的代码,即使是相同序列的代码。那么所有列都有相同长度的转义码。

BEGIN { FS=OFS="|" }  
function colour(ss,cc)    { return "\033[" cc ";1m" ss "\033[0m"; }
function notcolour(ss,cc) { return "\033[" cc ";0m" ss "\033[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 
}

(上面还应用了许多小的简化和更正,包括匹配源数据更改的一项。)

这种方法的问题在于它取决于您的columnlibc。 (我来自 util-linux-2.23.2)当发现不可打印内容时,column不会检查返回码为-1,而不是实际宽度;wcswidth()这确实搞乱了表格格式。 util-linux-2.30.1 的最新版本使用了新的它解决了这个问题,但它通过用十六进制编码版本替换不可打印的内容来实现这一点\x- 所以你完全失去了原始转义:/你可以用不优雅的方式修复:

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

whereprintf解释转义。您可以在自己的代码中替换\033\\x1b以获得相同的效果。我不确定你是否使用 Linux。)

对于第三个选项,您需要支持column设置-o输出分隔符,默认为两个空格。将其设置为“ |”,然后你可以使用这个:

curl ... | column -t -s "|" -o "|" | awk '
BEGIN { FS="|" }  
function colour(ss,cc) { return sprintf("\033[%i;1m%s\033[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 
}'

这里的技巧是我们使用column管道分隔的输入输出,它修复了宽度,我们可以使用 安全地处理它awk,保留所有重要的空间。如果你column不支持,-o你可以用以下方法伪造它:

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

这将分隔符加倍为“ ^|”、columnuses^和 awkuses |。这使得假设^当然没有出现在数据中。硬选项卡可能会起作用。

我想你现在知道“为什么”了,但要明确的是:

  • columnstrlen()可能会天真地用/来计算八位字节(或字符)wcslen(),这与终端呈现的长度不匹配
  • column可能使用 来计算长度isprint(),对于终端转义也是不正确的
  • column当遇到不可打印的内容时,可能会放弃(就像我的那样)任何列

虽然剥离颜色代码序列是一个相当简单的问题,但如果column.

答案2

这段代码是从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"

产生:

-----
 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)

这里使用mr.spuratic的方法3,colorize专栏化。

我删除了一个字段以使其更易于阅读,然后应用了 2 个脚本(使用前缀重命名)我的-此处)来自线程如何仅对 bash 脚本的某些关键字进行着色? -- 两者都是通过给字符串着色来工作的诺尔恰运行列后。 (如果有人能指出我在帖子中显示颜色的方法,我将不胜感激。)

我假设这些字符串不会出现在输出中的其他位置,因此特定字段不是问题,将检查整行是否匹配。如果情况并非如此,那么除了引起对脚本的注意之外,该解决方案没有什么价值海力达强调

这是在如下系统上完成的:

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

如果您使用脚本强调,你需要 spc (在 Debian 包中超级猫);以下是一些相关细节:

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

最美好的祝愿...干杯,drl

答案3

既然大家都已经解释过了为什么 column显示未对齐的输出,我不会再解释它。

相反,我会展示另一种解决方案

对于任何有兴趣获得column类似输出但具有颜色代码美国国家标准协会转义序列,我制作了一个版本,其中包含相同的语法as column(对于支持的标志)并且它呈现输出符合预期

我在网上找到了各种解决方案,但它们都Bash用于循环,相比之下非常慢Perl,我用过。

你可以找到脚本这里

它可以直接执行 ( ./column_ansi.sh [--options]) 或源执行 ( source ./column_ansi.sh,在这种情况下,该命令将可用为column_ansi)

相关内容