Vẽ biểu đồ từ đầu ra lệnh bash


31

Tôi có đầu ra sau:

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

Và tôi muốn vẽ một biểu đồ

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

Bạn có biết nếu có một lệnh bash sẽ cho phép tôi làm điều đó?


1
bashplotlib là một giải pháp tốt đẹp
Michael Mior

Đó thực sự là một trong những rủi ro của việc cung cấp liên kết thay vì câu trả lời độc lập. Nếu câu trả lời SO bị xóa là hữu ích, xin vui lòng gửi nó như một câu trả lời ở đây.
Jeff Schaller

Câu trả lời:


12

Hãy thử điều này trong :

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

GIẢI THÍCH:

  • -alà một rõ ràng split()trong @Fmảng, chúng tôi nhận được giá trị với$F[n]
  • x là nói với perl để in một ký tự N lần
  • ($F[1] / 5) : ở đây chúng tôi lấy số và chia cho 5 cho một đầu ra in đẹp

1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'Trông thật tuyệt :) cảm ơn
Natim 7/1/2015

12

Trong perl:

perl -pe 's/ (\d+)$/"="x$1/e' file
  • elàm cho biểu thức được ước tính, vì vậy tôi nhận được =lặp lại bằng cách sử dụng giá trị của $1(số khớp với (\d+)).
  • Bạn có thể làm "="x($1\/3)thay vì "="x$1để có được dòng ngắn hơn. (Cái /này được thoát ra vì chúng ta đang ở giữa một lệnh thay thế.)

Trong bash(lấy cảm hứng từ câu trả lời SO này ):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printfđệm chuỗi thứ hai bằng cách sử dụng khoảng trắng để có chiều rộng $n ( %${n}s) và tôi thay thế khoảng trắng bằng =.
  • Các cột được phân định bằng cách sử dụng một tab ( \t), nhưng bạn có thể làm cho nó đẹp hơn bằng cách chuyển sang column -ts'\t'.
  • Bạn có thể sử dụng $((n/3))thay vì ${n}để có được dòng ngắn hơn.

Phiên bản khác:

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

Hạn chế duy nhất tôi có thể thấy là bạn sẽ cần chuyển sedđầu ra của một thứ gì đó nếu bạn muốn thu nhỏ lại, nếu không đây là tùy chọn sạch nhất. Nếu có cơ hội tệp đầu vào của bạn chứa một trong số [?*bạn nên dẫn lệnh w / set -f;.


2
Bravo cho thấy một giải pháp vỏ quá. Giải pháp Perl của bạn là rất sạch sẽ là tốt.
gà con

@mikeerv Tuyệt vời! Tôi luôn quên %*sdù đó là printfmẹo liên quan đầu tiên tôi học được trong lập trình C.
muru

Các printf(sed) | trphiên bản không làm việc ở đây như xa như tôi có thể nói.
Natim

@Natim đây đang ở đâu?
muru

@mikeerv hạn chế về độ dài đối số có lẽ?
muru

6

Dễ dàng với awk

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

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

Hoặc với ngôn ngữ lập trình yêu thích của tôi

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

3

Làm thế nào về:

#! /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

Sản xuất:

~/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

Điều này đánh tôi như là một vấn đề dòng lệnh truyền thống thú vị. Đây là bashgiải pháp kịch bản của tôi :

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");}'

Kịch bản nhỏ ở trên giả định dữ liệu nằm trong một tệp có tên tưởng tượng là "dữ liệu".

Tôi không quá hài lòng với dòng "chạy nó qua sed và sort" - sẽ không cần thiết nếu tháng và ngày của bạn luôn có 2 chữ số, nhưng đó là cuộc sống.

Ngoài ra, như một ghi chú lịch sử, các Unix truyền thống thường đi kèm với tiện ích vẽ sơ đồ dòng lệnh có thể thực hiện các đồ thị và đồ thị ASCII khá xấu xí. Tôi không thể nhớ tên, nhưng có vẻ như âm mưu GNU thay thế tiện ích truyền thống cũ.


Có nên như if ($1 in count) ...vậy không?
muru

1
@muru - dường như làm việc một trong hai cách. Tuy nhiên, tôi đã tìm thấy một lỗi đánh máy trong mệnh đề "khác". Cảm ơn.
Bruce Ediger

1

Tập thể dục tốt đẹp ở đây. Tôi đã đổ dữ liệu vào một tệp có tên là "dữ liệu" vì tôi rất giàu trí tưởng tượng.

Chà, bạn đã yêu cầu nó trong bash ... đây là trong bash thuần túy.

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

awk là một lựa chọn tốt hơn

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

Bạn có thể dẫn dữ liệu qua awk thay vì sử dụng một tập tin không?
Natim

Vâng, đó là điều tương tự theo cách nào. Chỉ cần thêm một "dữ liệu mèo |" lúc đầu như tôi đã có cho các bit bash hoặc "<data" ở cuối. Hoặc thậm chí bạn có thể chỉ có phần awk mà không cần tệp được chỉ định, dán dữ liệu và nhấn ctrl-D ở cuối. Chỉ định tệp chỉ coi tệp đó là stdin và tôi không muốn tiếp tục sao chép và dán tệp dữ liệu vì tôi lười biếng.
Falsenames 7/1/2015

1
Trên thực tế, tôi vừa đọc lại câu hỏi trong khi liên kết câu hỏi này với đồng nghiệp ... bạn nói rằng bạn đã "xuất", không phải là tệp dữ liệu. Vì vậy, bạn có thể chạy bất cứ thứ gì đang tạo ra báo cáo đó, sau đó chuyển nó thành awk, và bạn đã hoàn thành. Các đường ống chỉ là đầu ra trực tiếp của lệnh cuối cùng làm nguồn đầu vào cho lệnh tiếp theo.
Falsenames 7/1/2015

0

Thử đi:

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

Phần khó khăn duy nhất là việc xây dựng các thanh. Tôi làm điều đó ở đây bằng cách ủy thác printftrthích câu trả lời SO này .

Như một phần thưởng, đó là POSIX- sh-compliant.

Tài liệu tham khảo:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.