Skip to content

Instantly share code, notes, and snippets.

@jneidel
Last active April 26, 2025 19:44
Show Gist options
  • Save jneidel/bd8d96573f0f73ea662598a62cbb554a to your computer and use it in GitHub Desktop.
Save jneidel/bd8d96573f0f73ea662598a62cbb554a to your computer and use it in GitHub Desktop.
Migrate Neorg files to org-mode (.norg -> .org)

Migrate neorg files to orgmode files

I wanted to switch from nvim-neorg to emacs orgmode. The major hurdle for switching was not setting up orgmode, but migrating my thousands of files that I still had in the .norg format.

The script contained here is what I used to migrate all of my files at scale. In the test files you can see how different features will be transformed by the script. Enjoy.

#! /bin/sh
if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ "$1" = "help" ] || [ -z "$1" ]; then
cat <<EOF
$ migrate-norg-to-org DIR
Transform all .norg files in the directory into .org files.
Parameters:
\$1: dir to use this on
Flags
--remove-originals: delete migrated .norg files successful processing
Example:
$ migrate-norg-to-org .
$ migrate-norg-to-org ~/org --remove-originals
EOF
exit
fi
dir="$1"
removeOriginals=0
if [ "$2" = "--remove-originals" ]; then
printf "Are you sure that you want to remove the orignals after the migration? (y/N): "
read ans
if [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then
removeOriginals=1
else
echo "Aborting."
exit 0
fi
fi
transform_file_contents() {
block=
ol_n=
if [ "$(wc -l </dev/stdin)" -eq 0 ]; then # one line in the file not caught by IFS=
cat /dev/stdin
fi
cat /dev/stdin | while IFS= read -r line; do
line=$( echo "$line" |
sed "s|- (x) |- [X] |; s|- (-) |- [-] |; s|- (_) |- [_] |; s|- (.) |- [ ] |" | # checkboxes & states
sed "s|~ (x) |~ [X] |; s|~ (-) |~ [-] |; s|~ (_) |~ [_] |; s|~ (.) |~ [ ] |" | # checkboxes & states
sed 's/{\(.\+\)}\[\(.\+\)\]/[[\1][\2]]/; s/{\(.\+\)}/[\1]/' | # urls
sed "s|\`<->\`|<->|g" # personal idiocracy
)
if [ "$line" = "@document.meta" ]; then
block=meta
continue
fi
if echo "$line" | grep "^ *> " >/dev/null; then
if [ "$block" != "quote" ]; then
echo "#+BEGIN_QUOTE"
block=quote
fi
echo "$line" | sed 's|^ *> ||'
continue
fi
if echo "$line" | grep "^ *@code" >/dev/null; then
block_type=$(echo "$line" | grep -Po "@code \K.+")
if [ -z "$block_type" ]; then
block=code
echo "#+BEGIN_EXAMPLE"
else
block=$block_type
echo "#+BEGIN_SRC ${block}"
fi
continue
fi
if echo "$line" | grep "^ *@end" >/dev/null; then
if [ "$block" = "meta" ]; then
block=
continue
elif [ "$block" = "code" ]; then
echo "#+END_EXAMPLE"
else
echo "#+END_SRC ${block}"
fi
block=
continue
fi
# meta tags
if [ "$block" = "meta" ]; then
if echo "$line" | grep "^title" >/dev/null; then
title="$(echo "$line" | grep -Po "^title: \K.+")"
echo "#+TITLE: $title"
elif echo "$line" | grep "^created" >/dev/null; then
date="$(echo "$line" | grep -Po "^created: \K.+")"
echo "#+DATE: $date"
elif echo "$line" | grep "^deadline" >/dev/null; then # individual tag
date="$(echo "$line" | grep -Po "^deadline: \K.+")"
echo "#+DEADLINE: $date"
elif echo "$line" | grep "^_deadline" >/dev/null; then
date="$(echo "$line" | grep -Po "^_deadline: \K.+")"
echo "#+_DEADLINE: $date"
fi # discard every other meta tag
continue
fi
if [ -n "$block" ]; then
if [ "$block" = "quote" ]; then
if echo "$line" | grep "^ *$" >/dev/null; then
echo "#+END_QUOTE"
block=
echo ""
else
echo "$line" | sed 's|^ *||'
fi
continue
else # code blocks, don't manipulate them
echo "$line"
continue
fi
fi
line=$( echo "$line" |
sed "s|\`|~|g" # inline code
)
if echo "$line" | grep -P "^ *~+ " >/dev/null; then # ol
contents=$(echo "$line" | grep -Po "~+ \K.+")
if echo "$line" | grep -P "~{3}" >/dev/null; then
echo " + ${contents}" | sed "s|\`|~|g" # inline code
elif echo "$line" | grep -P "~{2}" >/dev/null; then
echo " - ${contents}" | sed "s|\`|~|g" # inline code
elif echo "$line" | grep -P "~{1}" >/dev/null; then
if [ -z "$ol_n" ]; then
ol_n=1
else
ol_n=$(($ol_n +1))
fi
echo "${ol_n}. ${contents}" | sed "s|\`|~|g" # inline code
fi
continue
fi
if echo "$line" | grep -P "^ *-+ " >/dev/null; then # ul
contents=$(echo "$line" | grep -Po "\-+ \K.+")
if echo "$line" | grep -P "\-{4}" >/dev/null; then
echo " + ${contents}"
elif echo "$line" | grep -P "\-{3}" >/dev/null; then
echo " - ${contents}"
elif echo "$line" | grep -P "\-{2}" >/dev/null; then
echo " + ${contents}"
elif echo "$line" | grep -P "\-{1}" >/dev/null; then
echo "- ${contents}"
fi
continue
fi
echo "$line" | # indentation is not removed, because I'm not checking if part of ul/ol
sed "s|\`|~|g" # inline code
if [ -z "$line" ]; then
ol_n= # reset
fi
done
# not covered
# - strike-through
# - file ending in a quote
}
transform_file() {
norgFile="$1"
filenameNoExt="${norgFile%.*}"
orgFile="${filenameNoExt}.org"
echo "Processing: $norgFile"
transform_file_contents <"$norgFile" >"$orgFile"
}
matched_files=$(mktemp -u)
match_files() {
if [ ! -e "$matched_files" ]; then
find "$dir" -type f -name "*.norg" >"$matched_files"
fi
cat "$matched_files"
}
match_files | while read file; do
printf "\n" >> "$file"
transform_file "$file"
done
if [ "$removeOriginals" -eq 1 ]; then
echo "Removing orignals"
match_files | while read file; do
rm "$file"
done
fi
@document.meta
title: test file
created: 2024-11-08
updated: 2024-11-08
@end
* H1
- ( ) /Todo/
- (-) *In progress*
- (x) `Done`
- (_) -Deleted-
- ul
-- nested
--- 3 deep
> quote me this
quote me that
quote me that
No longer quoting
** H2
{http://localhost:1313/now/}
{http://localhost:1313/now/}[outside]
~ {http://localhost:1313/now/}[inside]
~~ ul
some text
~~ `nested`
~~~ (x) l3
~ no2
~~ ( ) no1 `<->`
~ reset
*** H3
@code javascript
console.log(`${1+1}`)
@end
@code
123
@end
***** H5

test file

H1

  • [ ] Todo
  • [-] In progress
  • [X] Done
  • [_] -Deleted-
  • ul
    • nested
      • 3 deep

quote me this quote me that quote me that

No longer quoting

H2

[http://localhost:1313/now/] outside

  1. inside
    • ul some text
    • nested
      • [X] l3
  2. no2
    • [ ] no1 <->
  3. reset

H3

console.log(`$[1+1]`)
123
H5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment