Skip to content

Commit 8865b77

Browse files
committed
simple ascii table
1 parent a4e04f7 commit 8865b77

File tree

4 files changed

+206
-2
lines changed

4 files changed

+206
-2
lines changed

README.adoc

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
= bash-ascii-table
2+
3+
Display tabular data in ascii table works with bash 3.2 or later
4+
5+
== Usage
6+
Copy function print_table into your script, profit.
7+
8+
Example:
9+
10+
print_table ";" \
11+
"h0*;h1;h2" \
12+
"g0;[0, 1];[0, 2]" \
13+
"g0;[1, 1];[1, 2]" \
14+
"g1;[2, 1];[2, 2]" \
15+
"g1;[2, 1];[2, 2]" \
16+
"g1;[3, 1];[3, 2]"
17+
18+
Will print this table:
19+
20+
| h0 | h1 | h2 |
21+
|----+--------+--------|
22+
| g0 | [0, 1] | [0, 2] |
23+
| | [1, 1] | [1, 2] |
24+
|----+--------+--------|
25+
| g1 | [2, 1] | [2, 2] |
26+
| | [2, 1] | [2, 2] |
27+
| | [3, 1] | [3, 2] |
28+
29+
See: demo.sh for more usage examples.

README.md

-2
This file was deleted.

asciitable.sh

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
2+
# args: filed_separator, header, row_0, row_1 .. row_N
3+
# header and each row is single string of fields separted by filed separator
4+
# provided as first argument
5+
# fields can use ANSI escape code get colour output
6+
# see: https://en.wikipedia.org/wiki/ANSI_escape_code
7+
# e.g. to get green text set value: \e[38;5;76mGREEN TEXT\e[0m
8+
# header fields can have formatting options
9+
# * : group by column (note data MUST be odered by that column)
10+
# < : left align (default)
11+
# > : right align
12+
# - : TODO center align
13+
# e.g. print_table ";" "h1*<;h2;h3>" "g1;val 1,1;val 1,2" "g1;val 2,1;val 2,2" "g2;val 3,1;val 3,2"
14+
# will print:
15+
# | h1 | h2 | h3 |
16+
# |----+---------+---------|
17+
# | g1 | val 1,1 | val 1,2 |
18+
# | | val 2,1 | val 2,2 |
19+
# |----+---------+---------|
20+
# | g2 | val 3,1 | val 3,2 |
21+
function print_table() {
22+
local field_sep=${1}
23+
shift
24+
local row_count=${#}
25+
local rows=("${@}")
26+
27+
# parse header to get initial col widths and formatting
28+
IFS=${field_sep} read -r -a header <<< "${rows[0]}"
29+
local col_count="${#header[@]}"
30+
declare -a col_width
31+
declare -a col_align
32+
local group_column=-1
33+
local i
34+
for ((i=0;i<col_count;i++)); do
35+
local h=${header[${i}]}
36+
# get header name by stripping formatting
37+
local name=${h%%[<>*]*}
38+
# get format dierctives
39+
local frmt=${h:${#name}}
40+
header[${i}]=${name}
41+
col_width[${i}]=${#name}
42+
if [[ -n "${frmt}" ]];then
43+
if [[ ${frmt} == *'*'* ]];then
44+
group_column=${i}
45+
fi
46+
if [[ ${frmt} == *'>'* ]];then
47+
col_align[${i}]=""
48+
else
49+
col_align[${i}]="-"
50+
fi
51+
else
52+
col_align[${i}]="-"
53+
fi
54+
done
55+
56+
# iterate over all rows to find max width for each column
57+
# also collect field data width info i.e. width of data without escape sequences
58+
# color escape
59+
local color_esc=$(printf '%b' '\e')
60+
local color_re='(.*)\\e(\[[0-9;]+m)(.*)'
61+
# for each row holds info field raw text width (after removing color escape sequnces)
62+
declare -a field_widths_rows
63+
for ((i=1;i<row_count;i++)); do
64+
IFS=${field_sep} read -r -a fields <<< "${rows[${i}]}"
65+
local row_esc=
66+
local width_row=
67+
local j
68+
for ((j=0;j<col_count;j++)); do
69+
local field_esc=${fields[${j}]}
70+
local field=${fields[${j}]}
71+
while [[ ${field} =~ ${color_re} ]]; do
72+
field=${BASH_REMATCH[1]}${BASH_REMATCH[3]}
73+
done
74+
while [[ ${field_esc} =~ ${color_re} ]]; do
75+
field_esc=${BASH_REMATCH[1]}${color_esc}${BASH_REMATCH[2]}${BASH_REMATCH[3]}
76+
done
77+
if [[ j -gt 0 ]]; then
78+
width_row="${width_row};"
79+
row_esc="${row_esc}${field_sep}"
80+
fi
81+
width_row="${width_row}${#field}"
82+
row_esc="${row_esc}${field_esc}"
83+
# keep track of column max width, take into account length witout any escape sequences
84+
if [[ ${col_width[${j}]} -lt ${#field} ]]; then
85+
col_width[${j}]=${#field}
86+
fi
87+
done
88+
rows[${i}]=${row_esc}
89+
field_widths_rows[${i}]="${width_row}"
90+
done
91+
92+
local row_fmt="|"
93+
for ((i=0;i<col_count;i++)); do
94+
row_fmt="${row_fmt} %${col_align[${i}]}${col_width[${i}]}.${col_width[${i}]}s |"
95+
done
96+
97+
# build divider
98+
local div="|-$(printf "%${col_width[0]}s" | tr " " "-")-"
99+
for ((i=1;i<col_count;i++)); do
100+
div="${div}+-$(printf "%${col_width[${i}]}s" | tr " " "-")-"
101+
done
102+
div="${div}|"
103+
104+
# print header
105+
printf "${row_fmt}\n" "${header[@]}"
106+
if [[ ${group_column} -lt 0 ]];then
107+
echo "${div}"
108+
fi
109+
110+
# use separator as uninitialized value for group as can't be valid value
111+
local current_group=${field_sep}
112+
for ((i=1;i<row_count;i++)); do
113+
IFS=${field_sep} read -r -a fields <<< "${rows[${i}]}"
114+
IFS=';' read -r -a field_widths <<< "${field_widths_rows[${i}]}"
115+
116+
if [[ ${group_column} -ge 0 ]];then
117+
if [[ ${fields[${group_column}]} != ${current_group} ]];then
118+
echo "${div}"
119+
current_group=${fields[${group_column}]}
120+
else
121+
fields[${group_column}]=""
122+
fi
123+
fi
124+
125+
row_fmt="|"
126+
for ((j=0;j<col_count;j++)); do
127+
local width
128+
if [[ ${j} -eq ${group_column} && ${fields[${group_column}]} == "" ]]; then
129+
width=${col_width[${j}]}
130+
else
131+
width=${col_width[${j}]}
132+
local field_esc_width=${#fields[${j}]}
133+
local field_width=${field_widths[${j}]}
134+
if [[ ${field_esc_width} != ${field_width} ]];then
135+
width=$((field_esc_width+width-field_width))
136+
fi
137+
fi
138+
row_fmt="${row_fmt} %${col_align[${j}]}${width}.${width}s |"
139+
done
140+
printf "${row_fmt}\n" "${fields[@]}"
141+
done
142+
}

demo.sh

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
3+
base_dir=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
4+
5+
source ${base_dir}/asciitable.sh
6+
7+
printf "\nSimple table\n\n"
8+
9+
print_table ';' \
10+
"h0;h1;h2" \
11+
"[0, 0];[0, 1];[0, 2]" \
12+
"[1, 0];[1, 1];[1, 2]" \
13+
"[2, 0];[2, 1];[2, 2]" \
14+
"[3, 0];[3, 1];[3, 2]"
15+
16+
17+
printf "\n\nGroup column\n\n"
18+
19+
print_table ";" \
20+
"h0*;h1;h2" \
21+
"g0;[0, 1];[0, 2]" \
22+
"g0;[1, 1];[1, 2]" \
23+
"g1;[2, 1];[2, 2]" \
24+
"g1;[2, 1];[2, 2]" \
25+
"g1;[3, 1];[3, 2]"
26+
27+
28+
printf "\n\nText alignment\n\n"
29+
30+
print_table ";" \
31+
"h0*;left<;right>" \
32+
"[0, 0];[0, 1];[0, 2]" \
33+
"[1, 0];[1, 1];[1, 2]" \
34+
"[2, 0];[2, 1];[2, 2]" \
35+
"[3, 0];____[3, 1]____;____[3, 2]____"

0 commit comments

Comments
 (0)