一个能够生成 Markdown 表格的 Bash 脚本
哈喽大家好,我是咸鱼。
今天分享一个很实用的 bash 脚本,可以通过手动提供单元格内容和列数或者将带有分隔符的文件(如 CSV、TSV 文件)转换为 Markdown 表格。
源代码在文末哦!原文链接:https://josh.fail/2022/pure-bash-markdown-table-generator/
具体功能:
- 手动生成表格:允许用户输入表格内容和列数,生成标准的 Markdown 格式表格。
- 文件转换:可以将 CSV 或 TSV 文件转换为 Markdown 表格。
- 选项支持:支持指定列数、设置分隔符、解析 CSV/TSV 文件等选项。
主要选项:
-COLUMNS
:设置生成表格的列数。-sSEPARATOR
:自定义输入文件的列分隔符。--csv
和--tsv
:分别用于解析 CSV 和 TSV 文件。
几个月前,我想要一个便携式的 Markdown 表格生成器,于是写了这个 markdown-table
脚本。
一开始我只是想传入一堆参数和列数,并让它生成相应的 Markdown 表格。就像下面的例子:
markdown-table -4 \
"Heading 1" "Heading 2" "Heading 3" "Heading 4" \
"Hi" "There" "From" "Markdown\!" \
"Everything" "Is" "So" "Nicely Aligned\!"
当我实现了这一功能后,我意识到还可以添加支持解析带有自定义分隔符的文件,比如 CSV 或 TSV。
markdown-table --tsv < test.tsv
上面两种方法都会生成一个 Markdown 表格:
| Heading 1 | Heading 2 | Heading 3 | Heading 4 |
| ---------- | --------- | --------- | -------------- |
| Hi | There | From | Markdown |
| Everything | Is | So | Nicely Aligned |
#!/usr/bin/env bash
# Usage: markdown-table -COLUMNS [CELLS]
# markdown-table -sSEPARATOR < file
#
# NAME
# markdown-table -- generate markdown tables
#
# SYNOPSIS
# markdown-table -COLUMNS [CELLS]
# markdown-table -sSEPARATOR < file
#
# DESCRIPTION
# markdown-table helps generate markdown tables. Manually supply arguments
# and a column count to generate a table, or pass in a delimited file to
# convert to a table.
#
# OPTIONS
# -COLUMNS
# Number of columns to include in output.
#
# -sSEPARATOR
# String used to separate columns in input files.
#
# --csv
# Shortcut for `-s,` to parse CSV files. Note that this is a "dumb" CSV
# parser -- it won't work if your cells contain commas!
#
# --tsv
# Shortcut for `-s$'\t'` to parse TSV files.
#
# -h, --help
# Prints help text and exits.
#
# EXAMPLES
# Build a 4 column markdown table from arguments:
# markdown-table -4 \
# "Heading 1" "Heading 2" "Heading 3" "Heading 4" \
# "Hi" "There" "From" "Markdown!" \
# "Everything" "Is" "So" "Nicely Aligned!"
#
# Convert a CSV file into a markdown table:
# markdown-table -s, < some.csv
# markdown-table --csv < some.csv
#
# Convert a TSV file into a markdown table:
# markdown-table -s$'\t' < test.tsv
# markdown-table --tsv < test.tsv
# Call this script with DEBUG=1 to add some debugging output
if [[ "$DEBUG" ]]; then
export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] '
set -x
fi
set -e
# Echoes given args to STDERR
#
# $@ - args to pass to echo
warn() {
echo "$@" >&2
}
# Print the help text for this program
#
# $1 - flag used to ask for help ("-h" or "--help")
print_help() {
sed -ne '/^#/!q;s/^#$/# /;/^# /s/^# //p' < "$0" |
awk -v f="$1" '
f == "-h" && ($1 == "Usage:" || u) {
u=1
if ($0 == "") {
exit
} else {
print
}
}
f != "-h"
'
}
# Returns the highest number in the given arguments
#
# $@ - one or more numeric arguments
max() {
local max=0 arg
for arg; do
(( ${arg:-0} > max )) && max="$arg"
done
printf "%s" "$max"
}
# Formats a table in markdown format
#
# $1 - field separator string
format_table() {
local fs="$1" buffer col current_col=0 current_row=0 min=3
local -a lengths=()
buffer="$(cat)"
# First pass to get column lengths
while read -r line; do
current_col=0
while read -r col; do
lengths["$current_col"]="$(max "${#col}" "${lengths[$current_col]}")"
current_col=$((current_col + 1))
done <<< "${line//$fs/$'\n'}"
done <<< "$buffer"
# Second pass writes each row
while read -r line; do
current_col=0
current_row=$((current_row + 1))
while read -r col; do
printf "| %-$(max "${lengths[$current_col]}" "$min")s " "$col"
current_col=$((current_col + 1))
done <<< "${line//$fs/$'\n'}"
printf "|\n"
# If this is the first row, print the header dashes
if [[ "$current_row" -eq 1 ]]; then
for (( current_col=0; current_col < ${#lengths[@]}; current_col++ )); do
printf "| "
printf "%$(max "${lengths[$current_col]}" "$min")s" | tr " " -
printf " "
done
printf "|\n"
fi
done <<< "$buffer"
}
# Main program
main() {
local arg cols i fs="##$$FS##"
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help) print_help "$1"; return 0 ;;
-[0-9]*) cols="${1:1}"; shift ;;
-s*) fs="${1:2}"; shift ;;
--csv) fs=","; shift ;;
--tsv) fs=$'\t'; shift ;;
--) shift; break ;;
-*) warn "Invalid option '$1'"; return 1 ;;
*) break ;;
esac
done
if [[ -z "$fs" ]]; then
warn "Field separator can't be blank!"
return 1
elif [[ $# -gt 0 ]] && ! [[ "$cols" =~ ^[0-9]+$ ]]; then
warn "Missing or Invalid column count!"
return 1
fi
{ if [[ $# -gt 0 ]]; then
while [[ $# -gt 0 ]]; do
for (( i=0; i < cols; i++ )); do
if (( i + 1 == cols )); then
printf "%s" "$1"
else
printf "%s%s" "$1" "$fs"
fi
shift
done
printf "\n"
done
else
cat
fi
} | format_table "$fs"
}
main "$@"