Self-compiling Markdown files by misusing shebangs
🔗Has this every happened to you?
You use pandoc
or some other markdown -> pdf or other
format a lot. Your command invocation is long since you add a lot of
configuration flags. You make it into a shell script to avoid typing the
same command everytime, but for it to be reusable you have to give the
.md
filename as an argument.
You, like me, might not be able to withstand all these previous seconds you feel you are waisting.
🔗Shebangs
You probably know that files that begin with #! ...
in
their first line are executed with the interpreter defined after the
shebang (#!
). The kernel, when called to execute something,
looks for a shebang header or an INTERP
field in the ELF
binary headers (unrelevant to his post). If the shebang exists, it
passes the control to that interpeter.
A familiar usage is starting python scripts with
#!/usr/bin/env python3
or similar.
So, we can use this to make the interpreter be the markdown compiling command:
On a markdown file, say document.md
:
#!/usr/bin/env -S bash -c 'pandoc --from markdown -V links-as-notes -V boxlinks -V pagestyle:empty --pdf-engine xelatex -V mainfont:'Inter-Medium' -V geometry:a6paper -t pdf -o "$(basename ${0} md)pdf" <(tail -n +2 "${0}")'
Dear reader,
This is a letter addressed to you, written in Markdown.
Author Name
Executing document.md
runs the command right away. If
your file is not executable, make it so with
chmod u+x document.md
.
Some minor explanations of the shell command:
$(basename ${0} md)
executes a shell subprocess. Thebasename
util strips prefixes and suffixes from paths. In this case it returnsdocument.
which along withpdf
makes the output filename.<(tail -n +2 "${0}")
executes a shell subprocess whose output you can use as a file argument.tail -n +N
skips the firstN
lines and returns the rest of the file. That’s how we strip the shebang before the pdf conversion.
🔗Huh??
PS: At first I was confused on why this worked; the kernel has a usually 127 or 255 byte length limit for the shebang interpreter. But it does something smart: if the first argument of the shebang is complete, it doesn’t have to bother with the bytes after the limit, since only the interpreter binary needs to know about the arguments. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c