A zero-dependency Bash implementation of md2ansi that converts Markdown to ANSI-colored terminal output.
This is a pure Bash implementation of the md2ansi markdown-to-ANSI formatter, designed to be compatible with the Python version while following strict Bash coding standards. The implementation is completely self-contained in a single executable file with no external dependencies beyond standard POSIX tools.
Key Features:
- ✓ Zero installation dependencies
- ✓ Single-file monolithic design
- ✓ Full markdown support (headers, lists, tables, code blocks, formatting)
- ✓ 256-color ANSI output
- ✓ Syntax highlighting for Python, JavaScript, and Bash
- ✓ Security features (file size limits, input sanitization, ReDoS protection)
- ✓ Compatible with Python version's command-line interface
# Clone the repository
git clone https://github.com/Open-Technology-Foundation/md2ansi.bash.git
cd md2ansi.bash
# Make scripts executable
chmod +x md2ansi md display-ansi-palette md-link-extract
# Test it
md2ansi --version
md2ansi README.mdSystem-wide installation (requires sudo):
make installThis installs to /usr/local by default:
- Executables →
/usr/local/bin/ - Man page →
/usr/local/share/man/man1/ - Bash completion →
/usr/local/share/bash-completion/completions/
User-local installation (no sudo required):
make install-localThis installs to ~/.local:
- Executables →
~/.local/bin/ - Man page →
~/.local/share/man/man1/ - Bash completion →
~/.local/share/bash-completion/completions/
Custom installation prefix:
make install PREFIX=/opt/md2ansiOther Makefile targets:
make uninstall # Remove installed files
make test # Run shellcheck validation
make clean # Remove temporary files
make help # Show help messageThe install.sh script provides an interactive installation experience:
# Interactive installation (prompts for location)
./install.sh
# System-wide installation
./install.sh --system
# User-local installation
./install.sh --user
# Custom prefix
./install.sh --prefix /opt/md2ansi
# Non-interactive mode
./install.sh --system --yesFeatures:
- ✓ Interactive prompts for installation type
- ✓ Automatic sudo elevation when needed
- ✓ Rollback on installation failure
- ✓ Shellcheck validation (if available)
- ✓ Clear post-installation instructions
For complete control over the installation:
# Install executables
sudo install -m 0755 md2ansi /usr/local/bin/
sudo install -m 0755 md /usr/local/bin/
sudo install -m 0755 display-ansi-palette /usr/local/bin/
sudo install -m 0755 md-link-extract /usr/local/bin/
# Install manpage
sudo install -m 0644 md2ansi.1 /usr/local/share/man/man1/
sudo mandb # Update man database
# Install bash completion
sudo install -m 0644 md2ansi.bash_completion /usr/local/share/bash-completion/completions/md2ansi
# Verify installation
md2ansi --version
man md2ansiView the manual page:
man md2ansiFor user-local installation, ensure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"Add to ~/.bashrc or ~/.bash_profile to make permanent.
Enable bash completion (user-local only):
Add to ~/.bashrc:
if [ -f ~/.local/share/bash-completion/completions/md2ansi ]; then
. ~/.local/share/bash-completion/completions/md2ansi
fi# View a markdown file
md2ansi README.md
# With pagination (recommended for long files)
md README.md
# Process from stdin
cat README.md | md2ansi
echo "# Hello **World**" | md2ansi
# Multiple files
md2ansi file1.md file2.md file3.md
# View the manual
man md2ansi# Read documentation in color
md /usr/share/doc/bash/README
# Preview your markdown before committing
md2ansi CHANGELOG.md | less -R
# View remote markdown files
curl -s https://raw.githubusercontent.com/user/repo/main/README.md | md2ansi
# Check how your documentation will render
./md README.mdHeaders (H1-H6) with distinct color gradients
- Bright yellow (H1) to dark gray (H6)
- Inline formatting supported within headers
Inline Formatting:
- ✓ Bold (
**text**) - ✓ Italic (
*text*or_text_) - ✓ Combined bold+italic (
***text***) - ✓ Strikethrough (
~~text~~) - ✓ Inline code (
`code`) - ✓ Links (
[text](url)) with underline formatting - ✓ Images (
) displayed as placeholders
Lists:
- ✓ Unordered lists (
-or*) - ✓ Ordered lists (
1.,2., etc.) - ✓ Task lists (
- [ ]and- [x]) - ✓ Nested lists with proper indentation
- ✓ Inline formatting within list items
Tables:
- ✓ Pipe-delimited tables with alignment support
- ✓ Left, center, and right alignment (
:---,:---:,---:) - ✓ Inline formatting in cells
- ✓ Proper borders and spacing
- ✓ Auto-sizing columns
Code Blocks:
- ✓ Fenced code blocks (
```and~~~) - ✓ Syntax highlighting for Python, JavaScript, and Bash
- ✓ Language detection and aliases (py, js, sh)
- ✓ Comment and keyword highlighting
Additional Elements:
- ✓ Blockquotes (
>) - ✓ Horizontal Rules (
---,===,___) - ✓ Footnotes (
[^1]references and[^1]: textdefinitions)
Advanced Features:
- ✓ ANSI-aware text wrapping
- ✓ Terminal width auto-detection
- ✓ Feature toggles (--no-tables, --no-syntax-highlight, etc.)
- ✓ Plain text mode for non-ANSI terminals
- ✓ Debug mode with detailed traces
Security:
- ✓ File size limits (10MB maximum)
- ✓ Per-line length limits (100KB)
- ✓ Input sanitization (ANSI escape removal)
- ✓ ReDoS protection with timeout
- ✓ Proper signal handling
The md script wraps md2ansi output with less for comfortable viewing of long markdown files:
md README.md # View with pagination and color
md documentation/*.md # Browse multiple files sequentiallyFeatures:
- Automatic pagination using
less -FXRS - Auto-exit if content fits on one screen (-F)
- No terminal initialization (-X)
- Raw ANSI color support (-R)
- No line wrapping (-S)
Usage:
SPACE- Next pageb- Previous pageq- Quit/pattern- Searchn- Next search result
View all 256 ANSI colors supported by your terminal:
display-ansi-palette # Display color palette with codesOutput includes:
- Standard 16 colors (0-15)
- 6×6×6 color cube (16-231)
- Grayscale ramp (232-255)
- Color codes for each value
Useful for:
- Verifying terminal color support
- Choosing custom colors
- Testing ANSI rendering
- Terminal configuration debugging
Extract and list all URLs from markdown files:
md-link-extract README.md # Extract all links
md-link-extract docs/*.md | sort -u # Deduplicated links from multiple files
md-link-extract *.md | wc -l # Count total linksFeatures:
- ✓ Extracts inline links
[text](url) - ✓ Extracts bare URLs
<http://example.com> - ✓ Extracts reference-style links
[text][ref] - ✓ Automatic URL deduplication
- ✓ One URL per line for easy processing
Use cases:
- Link validation before publishing
- Finding broken documentation links
- Creating link inventories
- SEO analysis
# Show help
md2ansi --help
md2ansi -h
# Show version
md2ansi --version
md2ansi -V
# Enable debug mode (output to stderr)
md2ansi --debug README.md 2>debug.log
md2ansi -D file.md
# Force specific terminal width
md2ansi --width 100 README.md
md2ansi -w 80 file.md
# Plain text mode (all formatting disabled)
md2ansi --plain README.md
md2ansi -t file.mdSelectively disable specific features:
# Disable table formatting
md2ansi --no-tables doc.md
# Disable syntax highlighting
md2ansi --no-syntax-highlight code-heavy.md
# Disable footnotes
md2ansi --no-footnotes academic.md
# Disable task lists (checkboxes)
md2ansi --no-task-lists todo.md
# Disable image placeholders
md2ansi --no-images doc.md
# Disable link formatting
md2ansi --no-links plain.md
# Combine multiple toggles
md2ansi --no-tables --no-syntax-highlight --no-footnotes simple.md# Process file with custom width for narrow terminals
md2ansi -w 60 README.md
# Debug table parsing issues
md2ansi -D tables.md 2>&1 | grep -i table
# Plain mode for piping to log files
md2ansi --plain CHANGELOG.md >> release-notes.txt
# Disable heavy features for faster processing
md2ansi --no-syntax-highlight --no-tables large-file.mdInput:
# Main Title
## Subtitle
### Section Header
This is **bold**, *italic*, and ***bold italic*** text.
This is ~~strikethrough~~ and `inline code`.
Visit [GitHub](https://github.com) for more.Command:
echo "..." | md2ansiResult: Colored terminal output with distinct header colors, formatted text, and underlined links.
Input:
Shopping List:
- Apples
- Oranges
- Valencia oranges
- Blood oranges
- Bananas
Todo:
- [x] Write documentation
- [ ] Add tests
- [ ] Review codeCommand:
md2ansi shopping.mdResult: Nested bullets with proper indentation, checked and unchecked task boxes.
Input:
| Feature | Status | Priority |
|:--------|:------:|---------:|
| Headers | ✓ | High |
| Tables | ✓ | Medium |
| Code | ✓ | High |Command:
md2ansi features.mdResult: Bordered table with left, center, and right aligned columns.
Input:
```python
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
```Command:
md2ansi code-example.mdResult: Color-highlighted Python code with keywords, functions, and comments colored appropriately.
Input:
> This is a blockquote.
> It can span multiple lines.
>
> It can even have multiple paragraphs.Command:
md2ansi quote.mdResult: Indented quoted text with distinct background color.
Input:
This statement needs a citation[^1].
Another fact[^2] worth noting.
[^1]: Source: Academic Journal, 2024
[^2]: See documentation for detailsCommand:
md2ansi academic.mdResult: Footnote references inline, definitions rendered at bottom of document.
# Preview your project README
md README.md
# Check documentation before deployment
find ./docs -name "*.md" -exec md {} \;
# View system documentation in color
md /usr/share/doc/bash/README
# Quick reference for command output
man bash | col -b > bash.txt && md bash.txt
# Compare markdown rendering
diff <(md2ansi version1.md) <(md2ansi version2.md)| Aspect | Bash Version | Python Version | Notes |
|---|---|---|---|
| Installation | Copy one file | pip install |
Bash: zero dependencies |
| Startup Time | ~50ms | ~30ms | Bash slightly slower |
| Processing Speed | 2-3x slower | Baseline | Python faster for large files |
| File Size | 1,476 lines (45KB) | ~800 lines | Bash more verbose |
| Dependencies | None (coreutils only) | Python 3.7+ | Bash more portable |
| Design | Monolithic single file | Single module | Both self-contained |
| Features | 100% compatible | Full feature set | Identical CLI |
| Syntax Highlighting | Line-based regex | Token-based parser | Python more sophisticated |
| Error Handling | set -e + trap |
try/except | Different paradigms |
| ReDoS Protection | timeout command |
multiprocessing | Different approaches |
| Memory Usage | Very low | Low | Both efficient |
| Platform | Linux/Unix/macOS | Cross-platform | Python more portable |
| Security | Built-in limits | Built-in limits | Equivalent protection |
Recommendation:
- Use Bash version: Quick installation, zero dependencies, system integration
- Use Python version: Larger files (>5MB), complex syntax highlighting, Windows support
| Feature | md2ansi.bash | bat | glow | mdcat | rich |
|---|---|---|---|---|---|
| Zero Install | ✓ | ✗ | ✗ | ✗ | ✗ |
| Dependencies | None | Rust | Go | Rust | Python |
| Tables | ✓ | ✗ | ✓ | ✓ | ✓ |
| Syntax Highlight | Basic | ✓✓ | ✓ | ✓ | ✓✓ |
| Footnotes | ✓ | ✗ | ✗ | ✗ | ✓ |
| File Size | 45KB | ~5MB | ~10MB | ~2MB | varies |
| Startup | 50ms | 100ms | 200ms | 80ms | 150ms |
| Portable | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| Customizable | ✓ | ✓✓ | ✓ | ✓ | ✓✓ |
md2ansi.bash advantages:
- Truly zero dependencies (only coreutils)
- Single file deployment
- Full markdown spec support
- Footnote support
- Smallest file size
- Fast startup
When to use alternatives:
- bat: Need advanced syntax highlighting, line numbers, git integration
- glow: Want TUI navigation, emoji support, glamorous styling
- mdcat: Need image rendering in iTerm2/Kitty
- rich: Python ecosystem integration, complex formatting
md2ansi follows a monolithic single-file design for maximum portability and zero installation friction. All functionality is embedded in one executable file (md2ansi, 1,476 lines) with no external libraries or modules.
Design Benefits:
| Benefit | Description |
|---|---|
| Zero Installation Issues | No broken paths, missing dependencies, or version conflicts |
| Single File Deployment | Copy to /usr/local/bin/ and it works immediately |
| Faster Startup | No overhead from sourcing multiple files (~50ms) |
| Simpler Debugging | All code in one place for easy tracing and modification |
| Clean Architecture | Well-organized internal sections with clear markers |
| Maximum Portability | Works on any system with Bash 5.2+ and coreutils |
md2ansi.bash/
├── md2ansi # Main executable (1,476 lines, 45KB)
│ # ◉ All functionality in single file
├── md # Pagination wrapper (15 lines)
├── display-ansi-palette # Color palette viewer (72 lines)
├── md-link-extract # Link extractor (54 lines)
├── test/
│ ├── run_tests # Test suite runner (181 lines)
│ ├── test_basic.sh # Basic features (128 lines)
│ ├── test_code.sh # Code blocks (168 lines)
│ ├── test_tables.sh # Tables (96 lines)
│ ├── test_footnotes.sh # Footnotes (105 lines)
│ ├── test_wrapping.sh # Text wrapping (182 lines)
│ ├── test_edge_cases.sh # Edge cases (207 lines)
│ ├── test_options.sh # Feature toggles (96 lines)
│ └── test_security.sh # Security features (131 lines)
├── README.md # This file
└── LICENSE # GPL-3.0 license
Total Project Size:
- Main executable: 1,476 lines
- Utility scripts: 141 lines
- Test suite: 1,294 lines
- Total: 2,911 lines
The md2ansi script is organized into clearly marked sections:
| Section | Lines | Purpose |
|---|---|---|
| Script Header | 1-36 | Shebang, strict mode, metadata, global variables, state tracking |
| Utility Functions | 37-226 | Messaging, terminal detection, file validation, signal handling, string manipulation |
| ANSI Colors | 227-322 | Color constants, ANSI escape sequences, color detection, strip/sanitize functions |
| Inline Rendering | 323-710 | Bold, italic, strikethrough, links, inline code, text wrapping, header/list/blockquote rendering |
| Table Rendering | 711-1015 | Table parsing, alignment detection, column width calculation, table output rendering |
| Block Parsing | 1016-1250 | Main parser, code blocks, tables, headers, lists, footnotes, regular text processing |
| Main Functions | 1251-1476 | Argument parsing, file processing, main entry point, program invocation |
Each section is marked with clear header comments:
# ================================================================================
# ANSI Color Definitions
# ================================================================================Global State Variables:
# Configuration
TERM_WIDTH=0 # Auto-detected terminal width
MAX_FILE_SIZE=10485760 # 10MB file size limit
MAX_LINE_LENGTH=100000 # 100KB per-line limit
# Feature Flags
OPTIONS[footnotes]=1
OPTIONS[syntax_highlight]=1
OPTIONS[tables]=1
OPTIONS[task_lists]=1
OPTIONS[images]=1
OPTIONS[links]=1
# Parsing State
IN_CODE_BLOCK=0 # Are we inside a code fence?
CODE_FENCE_TYPE='' # ``` or ~~~
CODE_LANG='' # Language identifier
FOOTNOTES=() # Associative array of footnote definitions
FOOTNOTE_REFS=() # Array tracking footnote reference orderCore Processing Pipeline:
main()- Entry point, argument parsingprocess_file()- Read file/stdin into line arrayparse_markdown()- Main parsing loop (block-level)colorize_line()- Inline formatting (bold, italic, links)wrap_text()- ANSI-aware text wrapping- Output to stdout
Rendering Functions:
render_header()- H1-H6 with color gradientsrender_list_item()- Unordered listsrender_ordered_item()- Numbered listsrender_task_item()- Checkboxesrender_table()- Complete table parsing and renderingrender_code_line()- Code with syntax highlightingrender_blockquote()- Quoted textrender_hr()- Horizontal rulesrender_footnotes()- Footnote section at end
The project includes a comprehensive test suite with 8 test files covering all features:
| Test File | Lines | Coverage |
|---|---|---|
| run_tests | 181 | Test runner with assertion framework |
| test_basic.sh | 128 | Headers, inline formatting, lists, links, images, horizontal rules |
| test_code.sh | 168 | Fenced code blocks, syntax highlighting, language detection, code fence types |
| test_tables.sh | 96 | Table parsing, alignment (left/center/right), borders, inline formatting in cells |
| test_footnotes.sh | 105 | Footnote references, definitions, ordering, missing definitions |
| test_wrapping.sh | 182 | Text wrapping, ANSI-aware wrapping, terminal width handling |
| test_edge_cases.sh | 207 | Empty files, malformed input, edge cases, error handling |
| test_options.sh | 96 | Feature toggles, --no-* options, plain mode, option combinations |
| test_security.sh | 131 | File size limits, line length limits, input sanitization, ReDoS protection |
Total test coverage: 1,294 lines
# Run complete test suite
./test/run_tests
# Run individual test files
./test/test_basic.sh
./test/test_code.sh
./test/test_tables.sh
./test/test_footnotes.sh
./test/test_wrapping.sh
./test/test_edge_cases.sh
./test/test_options.sh
./test/test_security.shThe test suite uses a simple assertion-based framework:
# Example assertions
assert_equals "expected" "actual" "test name"
assert_contains "haystack" "needle" "test name"
assert_not_empty "$value" "test name"
assert_exit_code 0 "command" "test name"# Test basic rendering
echo "# Test\n\nThis is **bold** text." | ./md2ansi
# Test with real README
./md README.md
# Test color support
./display-ansi-palette
# Test link extraction
./md-link-extract README.md
# Test with debug output
./md2ansi -D README.md 2>debug.log
cat debug.log
# Test different terminal widths
./md2ansi -w 60 README.md
./md2ansi -w 120 README.md
# Test feature toggles
./md2ansi --no-tables --no-syntax-highlight README.md
./md2ansi --plain README.mdThis implementation strictly adheres to the Bash Coding Standard:
| Standard | Implementation |
|---|---|
| Error Handling | set -euo pipefail at start |
| Shell Options | shopt -s inherit_errexit shift_verbose extglob nullglob |
| Indentation | 2 spaces throughout (no tabs) |
| Variables | Type-specific declarations (declare -i, declare -a, declare -A) |
| Constants | readonly for immutable values |
| Scoping | local for all function variables |
| Conditionals | [[ ]] for tests, (( )) for arithmetic |
| Quoting | All variable expansions quoted (except in [[ ]] or (( ))) |
| Messaging | Standard functions: error, warn, info, debug, die |
| Cleanup | Signal handling with trap |
| EOF Marker | All scripts end with #fin followed by blank line |
| Increment | Use ((var+=1)) not ((var++)) (post-increment breaks set -e) |
Since md2ansi is monolithic, all edits are made to the single md2ansi file.
To add inline formatting:
Edit the colorize_line() function in the Inline Rendering section (lines 323-710):
# Example: Add underline support for __text__
sed -E "s/__([^_]+)__/${ANSI_UNDERLINE}\1${ANSI_RESET}/g"To add block-level elements:
Edit the parse_markdown() function in the Block Parsing section (lines 1016-1250):
# Example: Add definition lists
if [[ $line =~ ^:\ (.*)$ ]]; then
render_definition "${BASH_REMATCH[1]}"
continue
fiTo add syntax highlighting for new language:
Edit the render_code_line() and add a highlighting function:
highlight_ruby() {
local -- code="$1"
# Add Ruby-specific patterns
sed -E "s/\b(def|class|end|if|else)\b/${COLOR_KEYWORD}\1${COLOR_CODEBLOCK}/g" <<<"$code"
}# Enable debug output (to stderr)
./md2ansi -D file.md 2>debug.log
# View debug output
cat debug.log
# Real-time debug viewing
./md2ansi -D file.md 2>&1 | grep DEBUG
# Debug specific features
./md2ansi -D tables.md 2>&1 | grep -i tableDebug output includes:
- ◉ Terminal width detection
- ◉ File size validation
- ◉ Table parsing steps (column count, alignment)
- ◉ Regex operation timeouts
- ◉ Feature flag states
Before submitting changes:
- Follows Bash Coding Standard strictly
- All variables properly declared with types (
declare -i,declare -a, etc.) - Functions use
localfor all variables - No shellcheck warnings (
shellcheck md2ansi) - Proper error handling with meaningful exit codes
- Scripts end with
#finmarker - Comments for complex logic
- Test coverage for new features
- Documentation updated in README
- No performance regressions
| File Size | Processing Time | Notes |
|---|---|---|
| Small (<10KB) | ~100ms | ◉ Fast, instant feedback |
| Medium (100KB) | ~500ms | ◉ Good, responsive |
| Large (1MB) | ~3-5s | ◉ Acceptable for terminal viewing |
| Very Large (5-10MB) | ~15-30s | ◉ Slow but within limits |
Optimization target: Terminal viewing responsiveness, not batch processing speed.
| Metric | Value | Notes |
|---|---|---|
| Startup time | ~50ms | Library sourcing overhead |
| Processing speed | ~2-3x slower than Python | Acceptable for terminal use |
| Memory usage | Very low | Efficient line-by-line processing |
| File size limit | 10MB | Configurable via MAX_FILE_SIZE |
| Line length limit | 100KB | Safety limit for ReDoS protection |
| Terminal width | 20-500 columns | Bounds checking |
For reference, processing a 1MB markdown file:
- Python version: ~2 seconds
- Bash version: ~5 seconds
- Difference: Acceptable for interactive terminal use
Optimization notes:
- Single
sedinvocation with multiple patterns incolorize_line() - Minimal subprocess spawning
- Efficient regex patterns
- ANSI-aware text wrapping without repeated strip operations
| Feature | Implementation | Limit/Behavior |
|---|---|---|
| File Size Limits | Pre-processing validation with wc -c |
10MB maximum (configurable) |
| Line Length Limits | Per-line checking during processing | 100KB per line |
| Input Sanitization | ANSI escape sequence removal | All input cleaned via strip_ansi() |
| ReDoS Protection | timeout command wrapper for regex |
1 second timeout on complex patterns |
| Injection Prevention | Proper variable quoting throughout | All expansions quoted |
| Signal Handling | trap cleanup handlers |
Graceful Ctrl-C, proper exit |
| Bounds Checking | Terminal width validation | 20-500 column range |
| Directory Protection | Explicit file checks | Rejects directories |
| Permission Checks | Read permission validation | Clear error messages |
# File size is checked before processing
validate_file_size "$filepath" "$MAX_FILE_SIZE"
# Input is sanitized
code=$(sanitize_ansi "$code")
# Complex regex uses timeout
timeout 1 sed "s/$pattern/$replacement/g" <<<"$text"
# All variables are quoted
printf '%s\n' "$variable"
# Signal handlers ensure cleanup
trap 'cleanup $?' EXIT
trap 'cleanup 130' INTQ: Why use md2ansi.bash instead of the Python version?
A: The Bash version has zero installation dependencies and can be deployed as a single file. Perfect for system integration, minimal environments, or when you can't install Python packages.
Q: Will this work on macOS?
A: Yes, as long as you have Bash 5.2+ installed. macOS ships with Bash 3.2, so install via brew install bash.
Q: Can I use this in scripts and automation?
A: Absolutely. Use --plain mode for log files, or pipe to less -R for interactive viewing.
Q: How do I check if my terminal supports 256 colors?
A: Run tput colors. Should return 256 or higher. Also try ./display-ansi-palette to see all colors.
Q: Colors not showing?
A: Check terminal color support: tput colors (should be ≥256). Try setting TERM=xterm-256color.
Q: Script not found error?
A: Make sure you're in the repository directory or use the full path. Check that scripts are executable: chmod +x md2ansi md.
Q: Permission denied error?
A: Run chmod +x md2ansi md display-ansi-palette md-link-extract to make scripts executable.
Q: Output is garbled?
A: Your terminal may not support ANSI codes. Use --plain mode: md2ansi --plain file.md.
Q: Tables not rendering correctly?
A: Check that your table has proper alignment row (second row with ---, :---:, ---:). Or disable tables: --no-tables.
Q: Slow performance on large files?
A: For files >5MB, consider using the Python version. Or disable heavy features: --no-syntax-highlight --no-tables.
Q: Can I customize the colors?
A: Yes, edit the ANSI color constants in the md2ansi file (lines 227-322). Look for COLOR_H1, COLOR_KEYWORD, etc.
Q: Does it support GitHub-flavored markdown?
A: Most GFM features are supported: tables, task lists, strikethrough, fenced code blocks. Some advanced features (e.g., emoji shortcodes) are not implemented.
Q: Can I add syntax highlighting for other languages?
A: Yes, add a highlight_<language>() function in the Inline Rendering section and update the language case statement in render_code_line().
Q: What about nested formatting like bold italic in tables?
A: Supported. Inline formatting works within table cells, list items, headers, and blockquotes.
Q: Can I process from a URL?
A: Yes, pipe from curl: curl -s https://example.com/file.md | md2ansi
Q: How do I run the test suite?
A: Run ./test/run_tests from the project directory. Individual tests can be run directly: ./test/test_basic.sh.
Q: How do I contribute?
A: Follow the Bash Coding Standard strictly, run shellcheck, add tests, and update documentation.
Q: Why monolithic instead of modular?
A: Zero installation friction. One file to copy, no broken dependencies. The code is still well-organized with clear section markers.
Q: Can I use this as a library?
A: It's designed as a standalone executable, but you can source it and call parse_markdown directly if needed.
| Limitation | Impact | Workaround |
|---|---|---|
| Syntax Highlighting | Simpler than Python (line-based vs token-based) | Use Python version for complex code |
| Unicode Width | Depends on terminal Unicode support | Ensure UTF-8 terminal locale |
| Performance | ~2-3x slower than Python for large files | Use Python version for files >5MB |
| Nested Formatting | Some edge cases may render differently | Test with actual content |
| Windows Support | Requires WSL or Cygwin | Use Python version on native Windows |
| Emoji Rendering | No emoji shortcode expansion | Use actual emoji characters |
| Math Rendering | No LaTeX/MathJax support | Not planned for terminal renderer |
| Requirement | Tool/Process |
|---|---|
| Code Style | Follow Bash Coding Standard strictly |
| Linting | Run shellcheck md2ansi md test/*.sh with zero warnings |
| Testing | Add tests for new features, all tests must pass |
| Compatibility | Maintain CLI compatibility with Python version |
| Documentation | Update README for new features, keep accurate |
| Performance | No regressions, benchmark large files if changing parser |
# 1. Make changes to code
vim md2ansi
# 2. Run shellcheck (must pass with zero warnings)
shellcheck md2ansi md test/*.sh
# 3. Run test suite (all tests must pass)
./test/run_tests
# 4. Test manually with real files
./md README.md
./md2ansi -D test-file.md 2>debug.log
# 5. Commit changes
git add md2ansi test/*.sh README.md
git commit -m "Add feature: description"
git pushAll changes must:
- ✓ Follow Bash Coding Standard
- ✓ Pass shellcheck with zero warnings
- ✓ Pass all existing tests
- ✓ Include new tests for new features
- ✓ Update README documentation
- ✓ Use proper variable declarations and quoting
- ✓ Include error handling with meaningful messages
- ✓ End with
#finmarker
All tools are standard and available on any modern Linux/Unix system:
| Tool | Package | Purpose |
|---|---|---|
tput |
ncurses | Terminal capability detection, color support |
wc |
coreutils | File size validation |
sed |
coreutils | Regex substitution for formatting |
awk |
coreutils | Text processing (minimal usage) |
grep |
coreutils | Pattern matching (minimal usage) |
timeout |
coreutils | ReDoS protection wrapper |
less |
util-linux | Pagination in md wrapper |
stty |
coreutils | Terminal size detection fallback |
Zero additional dependencies required!
All tools are part of standard POSIX/GNU utilities installed by default on Linux, macOS (with Homebrew), and BSD systems.
GPL-3.0 - Same as parent Python project
See LICENSE file for full text.
- Based on the Python md2ansi implementation
- Designed for maximum portability and zero dependencies
- Built with readability and maintainability in mind
| Metric | Value |
|---|---|
| md2ansi Lines | 1,476 lines (45KB) |
| Total Scripts | 4 main + 1 test runner + 8 test files |
| Test Coverage | 1,294 lines across 8 test files |
| Total Project | 2,911 lines |
| Features | 15+ markdown elements |
| Dependencies | 0 (zero) |
| Installation | Single file copy |
| Architecture | Monolithic single-file design |
| Languages Highlighted | Python, JavaScript, Bash |
| Security Features | 7+ protections |
- Repository: https://github.com/Open-Technology-Foundation/md2ansi.bash
- Issues: https://github.com/Open-Technology-Foundation/md2ansi.bash/issues
- Python Version: https://github.com/Open-Technology-Foundation/md2ansi
- License: GPL-3.0
Status: ✓ Core implementation complete and functional
Version: 1.0.0
#fin