Skip to content

Instantly share code, notes, and snippets.

@bkataru
Created February 25, 2026 02:22
Show Gist options
  • Select an option

  • Save bkataru/4373b269d87a454ed33947f204ef6497 to your computer and use it in GitHub Desktop.

Select an option

Save bkataru/4373b269d87a454ed33947f204ef6497 to your computer and use it in GitHub Desktop.
Markdown Newline Escaping in Bash/CLI — why \n appears instead of line breaks and how to fix

Skill: Markdown Newline Escaping in Bash/CLI Commands

Problem

When creating GitHub PRs, issues, or comments via gh CLI commands with multi-line markdown content, newlines are being escaped as literal \n strings instead of actual line breaks.

Example of Broken Output

## Summary\n\nFix duplicate symbol errors...\n\n## Problem\n\nclip.cpp was being compiled twice...

Instead of proper markdown with actual line breaks.


Root Cause

When passing multi-line strings to shell commands (especially via tool calls or string interpolation), the shell or the tool interprets newlines incorrectly:

  1. Direct string interpolation: Passing multi-line strings directly as arguments
  2. Tool call escaping: Some tools escape newlines as \n literals
  3. Bash string handling: Unquoted or improperly quoted strings lose formatting
  4. Heredoc syntax errors: Missing quotes around EOF delimiter causes variable expansion

Solutions

✅ Solution 1: Use Heredocs with Proper Quoting

# CORRECT - quotes around EOF prevent interpretation
cat << 'EOF'
Line 1

Line 2
EOF

# WRONG - without quotes, variables get expanded
cat << EOF
$content
EOF

✅ Solution 2: Write to Temporary File First

# Create file with proper formatting
cat > /tmp/pr-body.md << 'MARKDOWN'
## Summary

Fix duplicate symbol errors.

## Changes

- Removed clip.cpp from server build
MARKDOWN

# Then use with gh CLI
gh pr create --body-file /tmp/pr-body.md

✅ Solution 3: Use gh CLI --body-file Flag

# BEST PRACTICE - avoids all escaping issues
gh pr create --body-file /tmp/pr-body.md
gh issue comment 123 --body-file /tmp/comment.md

✅ Solution 4: Use $'...' Syntax for Embedded Newlines

# For short strings
gh pr create --body $'## Summary\n\nFix errors.\n\n## Changes\n\n- Fixed'

❌ What NOT to Do

# WRONG - newlines become literal \n
gh pr create --body "## Summary

Fix errors."

# WRONG - backslash escaping
gh pr create --body "## Summary\n\nFix errors"

Detection

To detect if your markdown has this issue:

# Check raw PR body
gh pr view <number> --json body

# Test before posting
echo "$BODY_CONTENT"

# Look for literal \n in output

Symptoms:

  • Rendered output shows \n as text
  • No line breaks in markdown
  • Lists appear as single line

Best Practices

For GitHub CLI

  1. Always use --body-file for multi-line content
  2. Use heredocs with quoted EOF: << 'EOF'
  3. Test with echo before creating PRs

For General Bash

  1. Quote heredoc delimiters: << 'EOF' not << EOF
  2. Use $'...' for embedded newlines in short strings
  3. Avoid string concatenation with newlines

For Tool Calls (AI Agents)

  1. Write to file first, then reference
  2. Use heredoc syntax when available
  3. Test output before final submission
  4. Prefer --body-file over inline body

Complete Example

#!/bin/bash
# Create PR with proper markdown formatting

# Step 1: Write body to file using heredoc
cat > /tmp/pr-body.md << 'MARKDOWN'
## Summary

Fix duplicate symbol errors by removing duplicate clip.cpp compilation.

## Problem

The clip.cpp file was being compiled twice:
1. In build_llama.zig (llama library)
2. In build_server.zig (server executable)

This caused duplicate symbol errors on Linux/BSD/Windows.

## Solution

Removed clip.cpp from build_server.zig since it's already included in the llama library.

## Impact

- ✅ Linux builds (gnu, musl)
- ✅ Windows builds (x86_64, aarch64)  
- ✅ FreeBSD builds
- ✅ macOS builds (already working)

## Related

- Fixes CI failures in release workflow v0.3.1
MARKDOWN

# Step 2: Create PR using file
gh pr create \
  --title "fix: Remove duplicate clip.cpp from server build" \
  --body-file /tmp/pr-body.md \
  --base master \
  --head fix/duplicate-clip-symbols

# Cleanup
rm /tmp/pr-body.md

Why This Happens in AI Tool Calls

When AI agents execute tool calls with multi-line strings:

  1. The tool receives the string with escaped newlines (\n)
  2. Shell doesn't interpret \n as newline without special handling
  3. Result: literal backslash-n appears in output

Fix: Use file-based approach or proper heredoc syntax.


Related Skills

  • See: SKILL: oh-my-opencode (OMO) Ralph Wiggum Loop & Agent Invocation
  • See: SKILL: Spawning & Monitoring Background Coding Agents from Claude Code

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment