Michael Cordell's Blog

Bulk Org-Mode to Github Flavored Markdown

14 Apr 2019

I wanted to move my raw notes for blog post entries from org-mode into a markdown format suitable for ingestion by Jekyll. While you could do single exports of each org file, and then copy-paste as posts are written, this seemed inefficient and difficult to make after-conversion changes. So, I set about a way to convert a set of org files into a corresponding set of markdown files. My initial approach was to use elisp to bulk script the org-mode export. I then wrapped this in a shell script which moved the files to the destination folder. However, I ended up settling on an approach of using pandoc to convert the files wrapped in a shell script. This appears to be the superior approach since it only requires one script file and less variable maintenance. I’ll leave both approaches in the post on the off chance the former works better for someone.

Environment and Dependencies

Initial approach (elisp + shell script)

Shell script at ~/org/export_blog.el:

(eval-after-load "org"
  '(require 'ox-gfm nil t))
;;;###autoload
(require 'org)

(defun dired-org-to-markdown ()
  (let ((files
         (append
          (let ((default-directory "~/org/blog"))
            (mapcar #'expand-file-name
                    (file-expand-wildcards "**/*.org")))
          (let ((default-directory "~/org/blog"))
            (mapcar #'expand-file-name
                    (file-expand-wildcards "*.org")))
         )
         ))
    (mapc
     (lambda (f)
       (with-current-buffer
           (find-file-noselect f)
         (org-gfm-export-to-markdown)))
     files))
  )

(dired-org-to-markdown)

Wrapping shell script, embedded at ~/org/ :

#! /bin/bash
SRC="./blog"
DEST="/Users/michael/Library/Mobile Documents/com~apple~CloudDocs/"

emacs --batch --load=export_blog.el
rsync -zarv  --include="*/" --include="*.md" --exclude="*" "$SRC" "$DEST"

Notes

Final approach (pandoc shell script)

Shell script placed at ~/org/ :

#! /bin/bash

SRC="./blog"
DEST="/Users/michael/Library/Mobile Documents/com~apple~CloudDocs/blog"

SRC_ESCAPED=$(echo $SRC | sed -e 's/\.\//\\.\\\//g')
DEST_ESCAPED=$(echo $DEST | sed -e 's/\//\\\//g')

STATEMENT="s/^$SRC_ESCAPED/$DEST_ESCAPED/"

# Create mirror'd folder structure at destination
find "$SRC" -type d -exec sh -c 'echo $1 | sed -e "$0" | xargs -I}{ mkdir -p "}{"' "$STATEMENT" {} \;

# Convert each org file to markdown in the destination folder structure
find "$SRC" -iregex ".*org" -exec sh -c 'echo $1 | sed -e "s/\.org$/\.md/" -e "$0" | xargs -I}{ pandoc -s --wrap=none -f org --toc -t gfm $1 -o }{' "$STATEMENT" {} \;

This script first creates a similar folder structure to the source folders. As a second step, it looks through the source for org files and creates the destination md path via substitution with sed. If you are interested in debugging the output paths substitution (since this fairly specific to my setup), try:

find "$SRC" -iregex ".*org" -exec sh -c 'echo $1 | sed -e "s/\.org$/\.md/" -e "$0"' "$STATEMENT" {} \;

It then passes these output paths to pandoc (set to use github flavored markdown with a table of contents and no line wrapping).

comments powered by Disqus