从bash命令输出绘制直方图


31

我有以下输出:

2015/1/7    8
2015/1/8    49
2015/1/9    40
2015/1/10   337
2015/1/11   11
2015/1/12   3
2015/1/13   9
2015/1/14   102
2015/1/15   62
2015/1/16   10
2015/1/17   30
2015/1/18   30
2015/1/19   1
2015/1/20   3
2015/1/21   23
2015/1/22   12
2015/1/24   6
2015/1/25   3
2015/1/27   2
2015/1/28   16
2015/1/29   1
2015/2/1    12
2015/2/2    2
2015/2/3    1
2015/2/4    10
2015/2/5    13
2015/2/6    2
2015/2/9    2
2015/2/10   25
2015/2/11   1
2015/2/12   6
2015/2/13   12
2015/2/14   2
2015/2/16   8
2015/2/17   8
2015/2/20   1
2015/2/23   1
2015/2/27   1
2015/3/2    3
2015/3/3    2

我想画一个直方图

2015/1/7  ===
2015/1/8  ===========
2015/1/9  ==========
2015/1/10 ====================================================================
2015/1/11 ===
2015/1/11 =
...

你知道是否有一个bash命令可以让我做到这一点?


1
bashplotlib是一个不错的解决方案
Michael

这确实是提供链接而不是独立答案的风险之一。如果删除的SO答案有用,请在此处将其发布为答案。
杰夫·谢勒

Answers:


12

尝试一下:

perl -lane 'print $F[0], "\t", "=" x ($F[1] / 5)' file

说明:

  • -asplit()@F数组中是显式的,我们用$F[n]
  • x 告诉perl打印一个字符N次
  • ($F[1] / 5) :在这里,我们得到数字并将其除以5得到漂亮的打印输出

1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'它看起来真的很棒:)谢谢
Natim 2015年

12

perl

perl -pe 's/ (\d+)$/"="x$1/e' file
  • e导致对表达式进行求值,因此我=使用的值$1(与匹配的数字(\d+))重复执行。
  • 您可以这样做"="x($1\/3)而不是"="x$1缩短行数。(/由于我们位于替换命令的中间,所以已将其转义。)

bash(从此SO答案中得到启发):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printf使用空格填充第二个字符串以获得宽度$n%${n}s),然后用替换空格=
  • 列使用制表符(\t)分隔,但您可以通过管道连接到来使其更漂亮column -ts'\t'
  • 您可以使用$((n/3))代替${n}以获得较短的行。

另一个版本:

unset IFS; printf "%s\t%*s\n" $(sed 's/$/ =/' test.txt) | tr ' ' =

我能看到的唯一缺点是,sed如果要缩小比例,则需要将输出通过管道传递给某些对象,否则这是最干净的选择。如果输入文件中可能包含其中一个,[?*则应使用w /命令set -f;


2
Bravo也展示了外壳解决方案。您的Perl解决方案也非常干净。
小鸡

@mikeserv太好了!%*s即使这是printf我在C编程中学到的第一个技巧,我也总是忘记。
muru 2015年

printf(sed) | tr据我所知,此版本在这里不起作用。
Natim 2015年

@Natim在哪里?
muru

@mikeserv可能会限制参数长度?
muru

6

容易 awk

awk '{$2=sprintf("%-*s", $2, ""); gsub(" ", "=", $2); printf("%-10s%s\n", $1, $2)}' file

2015/1/7 ========
2015/1/8 =================================================
2015/1/9 ========================================
..
..

或使用我最喜欢的编程语言

python3 -c 'import sys
for line in sys.stdin:
  data, width = line.split()
  print("{:<10}{:=<{width}}".format(data, "", width=width))' <file

3

怎么样:

#! /bin/bash
histo="======================================================================+"

read datewd value

while [ -n "$datewd" ] ; do
   # Use a default width of 70 for the histogram
   echo -n "$datewd      "
   echo ${histo:0:$value}

   read datewd value
done

产生:

~/bash $./histogram.sh < histdata.txt
2015/1/7    ========
2015/1/8    =================================================
2015/1/9    ========================================
2015/1/10   ======================================================================+
2015/1/11   ===========
2015/1/12   ===
2015/1/13   =========
2015/1/14   ======================================================================+
2015/1/15   ==============================================================
2015/1/16   ==========
2015/1/17   ==============================
2015/1/18   ==============================
2015/1/19   =
2015/1/20   ===
2015/1/21   =======================
2015/1/22   ============
2015/1/24   ======
2015/1/25   ===
2015/1/27   ==
2015/1/28   ================
2015/1/29   =
2015/2/1    ============
2015/2/2    ==
2015/2/3    =
2015/2/4    ==========
2015/2/5    =============
2015/2/6    ==
2015/2/9    ==
2015/2/10   =========================
2015/2/11   =
2015/2/12   ======
2015/2/13   ============
2015/2/14   ==
2015/2/16   ========
2015/2/17   ========
2015/2/20   =
2015/2/23   =
2015/2/27   =
2015/3/2    ===
2015/3/3    ==
~/bash $

1

这让我震惊,这是一个有趣的传统命令行问题。这是我的bash脚本解决方案:

awk '{if (count[$1]){count[$1] += $2} else {count[$1] = $2}} \
        END{for (year in count) {print year, count[year];}}' data |
sed -e 's/\// /g' | sort -k1,1n -k2,2n -k3,3n |
awk '{printf("%d/%d/%d\t", $1,$2,$3); for (i=0;i<$4;++i) {printf("=")}; printf("\n");}'

上面的小脚本假定数据位于一个想象中的名为“数据”的文件中。

我对“通过sed和sort进行排序”这一行不太满意-如果您的月和月中的某天总是有2位数字,那将是不必要的,但这就是生活。

另外,作为历史记录,传统的Unix曾经带有命令行绘图实用程序,该实用程序可以执行相当难看的ASCII图形和绘图。我不记得这个名字了,但是看起来GNU plotutils取代了旧的传统工具。


那不是if ($1 in count) ...吗?
muru 2015年

1
@muru-似乎都可以工作。但是,我确实在“其他”子句中发现了一个错字。谢谢。
Bruce Ediger 2015年

1

很好的锻炼。我将数据转储到名为“数据”的文件中,因为我很有想象力。

好吧,您以bash要求它...这里是纯bash。

cat data | while read date i; do printf "%-10s " $date; for x in $(seq 1 $i); do echo -n "="; done; echo; done

awk是更好的选择。

awk '{ s=" ";while ($2-->0) s=s"=";printf "%-10s %s\n",$1,s }' data

您可以通过awk而不是使用文件通过管道传输数据吗?
纳蒂姆,2015年

是的,两者都是一样的。只需添加“猫数据|” 一开始就像我对bash位一样,或者在结尾处输入“ <data”。或者,您甚至可以只具有awk部分而没有指定文件,粘贴数据并在最后按ctrl-D。指定文件只是将文件视为stdin,而我不想一直复制和粘贴数据文件,因为我很懒。
虚假名称,2015年

1
实际上,我只是在将问题链接到同事时重读了这个问题……您说您拥有“输出”,而不是数据文件。这样,您就可以运行正在创建该报告的任何内容,然后将其通过管道传输到awk,就可以完成了。管道仅将上一条命令的直接输出作为下一条命令的输入源。
虚假名称,2015年

0

尝试这个:

while read value count; do
    printf '%s:\t%s\n' "${value}" "$(printf "%${count}s" | tr ' ' '=')"
done <path/to/my-output

唯一棘手的部分是酒吧的构造。我在这里通过委派printftr喜欢这样的答案来做到这一点

另外,它是POSIX sh兼容的。

参考文献:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.