This script generates a table of contents from a Markdown file, reading from stdin or a file and outputting to stdout.
Usage:
./generate-toc.sh [input_file]
cat input_file.md | ./generate-toc.shExample output:
This script generates a table of contents from a Markdown file, reading from stdin or a file and outputting to stdout.
Usage:
./generate-toc.sh [input_file]
cat input_file.md | ./generate-toc.shExample output:
| #!/usr/bin/env node | |
| /** | |
| * Markdown Table of Contents Generator | |
| * | |
| * This script generates a table of contents from a Markdown file, | |
| * reading from stdin or a file and outputting to stdout. | |
| * | |
| * Usage: | |
| * ./generate-toc.js [input_file] | |
| * cat input_file.md | ./generate-toc.js | |
| */ | |
| const fs = require('fs'); | |
| const readline = require('readline'); | |
| // Regular expression to match Markdown headings | |
| // Captures: | |
| // - group 1: heading level (number of #) | |
| // - group 2: heading text | |
| const headingRegex = /^(#{1,6})\s+(.+)$/; | |
| // Parse command line arguments | |
| const inputFile = process.argv[2]; | |
| // Setup input stream (file or stdin) | |
| const inputStream = inputFile | |
| ? fs.createReadStream(inputFile) | |
| : process.stdin; | |
| const rl = readline.createInterface({ | |
| input: inputStream, | |
| crlfDelay: Infinity | |
| }); | |
| // Store heading info | |
| const headings = []; | |
| // Process each line | |
| rl.on('line', (line) => { | |
| const match = line.match(headingRegex); | |
| if (match) { | |
| const level = match[1].length; | |
| const text = match[2].trim(); | |
| headings.push({ level, text }); | |
| } | |
| }); | |
| // Generate TOC after reading all lines | |
| rl.on('close', () => { | |
| console.log('## Table of Contents\n'); | |
| headings.forEach(heading => { | |
| // Skip level 1 (title) if present | |
| if (heading.level === 1) return; | |
| // Create anchor from heading text | |
| const anchor = createAnchor(heading.text); | |
| // Create indentation based on heading level | |
| const indent = ' '.repeat(heading.level - 2); | |
| // Output TOC line | |
| console.log(`${indent}- [${heading.text}](#${anchor})`); | |
| }); | |
| }); | |
| /** | |
| * Converts heading text to a GitHub-compatible anchor | |
| * | |
| * @param {string} text - The heading text | |
| * @returns {string} - The anchor string | |
| */ | |
| function createAnchor(text) { | |
| return text | |
| .toLowerCase() | |
| // Replace non-alphanumeric characters (except spaces) with nothing | |
| .replace(/[^\w\s-]/g, '') | |
| // Replace spaces with hyphens | |
| .replace(/\s+/g, '-') | |
| // Remove consecutive hyphens | |
| .replace(/-+/g, '-') | |
| // Remove leading/trailing hyphens | |
| .replace(/^-+|-+$/g, ''); | |
| } |
| #!/bin/bash | |
| # Markdown Table of Contents Generator | |
| # | |
| # This script generates a table of contents from a Markdown file, | |
| # reading from stdin or a file and outputting to stdout. | |
| # | |
| # Usage: | |
| # ./generate-toc.sh [input_file] | |
| # cat input_file.md | ./generate-toc.sh | |
| # Function to convert heading text to a GitHub-compatible anchor | |
| create_anchor() { | |
| echo "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^[:alnum:] -]//g' | tr ' ' '-' | sed -E 's/-+/-/g' | sed -E 's/^-+|-+$//g' | |
| } | |
| # Echo the TOC header | |
| echo "## Table of Contents" | |
| echo "" | |
| # Process input from file or stdin | |
| { | |
| if [ -n "$1" ] && [ -f "$1" ]; then | |
| cat "$1" | |
| else | |
| cat | |
| fi | |
| } | while IFS= read -r line || [ -n "$line" ]; do | |
| # Check if line is a heading (starts with #) | |
| if [[ "$line" =~ ^(#+)[[:space:]]+(.+)$ ]]; then | |
| heading_level="${#BASH_REMATCH[1]}" | |
| heading_text="${BASH_REMATCH[2]}" | |
| # Skip level 1 (title) if present | |
| if [ "$heading_level" -eq 1 ]; then | |
| continue | |
| fi | |
| # Create anchor from heading text | |
| anchor=$(create_anchor "$heading_text") | |
| # Create indentation based on heading level | |
| indent=$(printf "%$((($heading_level - 2) * 2))s" "") | |
| # Output TOC line | |
| echo "${indent}- [${heading_text}](#${anchor})" | |
| fi | |
| done |