Skip to content

Instantly share code, notes, and snippets.

@yeiichi
Created January 7, 2026 00:49
Show Gist options
  • Select an option

  • Save yeiichi/f64edd991adc11ca0a031aa055d5e1c8 to your computer and use it in GitHub Desktop.

Select an option

Save yeiichi/f64edd991adc11ca0a031aa055d5e1c8 to your computer and use it in GitHub Desktop.
Convert Markdown tables to CSV
#!/usr/bin/env bash
set -euo pipefail
# mdtable2csv: Convert Markdown tables to CSV.
# Handles alignment rows, trims cells, escapes CSV safely, and respects escaped pipes (\|).
mdtable2csv() {
local in out
if [[ $# -lt 1 || $# -gt 2 ]]; then
echo "Usage: mdtable2csv INPUT.md [OUTPUT.csv]" >&2
exit 2
fi
in="$1"
out="${2:-${in%.*}.csv}"
if [[ ! -f "$in" ]]; then
echo "ERROR: input file not found: $in" >&2
exit 1
fi
awk '
function trim(s) {
sub(/^[[:space:]]+/, "", s)
sub(/[[:space:]]+$/, "", s)
return s
}
function csv_escape(s, t) {
t = s
gsub(/"/, "\"\"", t)
if (t ~ /[,\n"]/)
return "\"" t "\""
return t
}
function is_separator_row(raw, s) {
s = raw
s = trim(s)
sub(/^\|/, "", s)
sub(/\|$/, "", s)
s = trim(s)
if (s !~ /-/) return 0
gsub(/[[:space:]\|\-:]/, "", s)
return (s == "")
}
BEGIN {
ESC = "\034"
}
# Optional stricter filter (recommended): uncomment to only process lines that start with a pipe
# $0 !~ /^[[:space:]]*\|/ { next }
/\|/ {
raw = $0
if (is_separator_row(raw)) next
line = raw
gsub(/\\\|/, ESC, line)
if (line !~ /\|/) next
line = trim(line)
sub(/^\|/, "", line)
sub(/\|$/, "", line)
n = split(line, a, /\|/)
row = ""
for (i = 1; i <= n; i++) {
field = a[i]
gsub(ESC, "|", field)
field = trim(field)
field = csv_escape(field)
row = row (i == 1 ? "" : ",") field
}
print row
}
' "$in" > "$out"
echo "Wrote: $out"
}
mdtable2csv "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment