Apollon Development

Posting terminal output in Ghost Blog

Almost every developer who shares his code on a blog stumbled upon a decision on how to share output from his console in a meaningful way. Sure, we have <pre> tags and markdown to help us, but sometimes we just can't get the colouring right, or we are missing line numbers for that matter.

In this short post I'll share a way which I developed for myself to use in this blog with a few tools. This is not an ultimate solution and there are dozens of different ways to achieve that but I found this one to be the easiest and fastest to use with Ghost blog engine.

Disclaimer: this solution was tested on MacOS only.

Gearing up

We'll need three tools to begin with

The plan is to record everything from your terminal using asciinema but instead of pushing it to the web and making a video of it, just save the raw asciinema output to a file, then parse it with terminal-to-html and finally strip out unnecessary html.

How-to

To record your terminal, run the following

asciinema rec --raw /tmp/rec_output.txt

Execute the command you want and stop recording by pressing ctrl+d. This will save exactly what you've seen in your terminal from the moment you ran the asciinema command until you pressed ctrl+d, exactly the way you've seen it, including the colours.

Next you want to translate the output to HTML, thus run

cat /tmp/rec_output.txt | terminal-to-html --preview

This will print full, standalone HTML page, however I just need the terminal part as I am going to paste it into a blog article into an HTML box. Because of that I want to have all the CSS-es already attached to my current Ghost template. You may want to use mine as an example.

The last part will be to strip the redundant HTML tags, and in my case also wrap every line with <span> for the sake of line proper line counting.

cat /tmp/rec_output.txt | terminal-to-html --preview \
| perl -0pe 's:\[J([1|2]?)::g' \ # Remove system escapes
| perl -0pe 's|.+?<div.+?>(.+?)<\/div>.+|\1|sg' \ # Get all from the only one <div>
| perl -0pe 's:\s*(.+):\1:gs' \ # Trim whitespaces from the BOF
| perl -0pe 'chomp' \ # Trim whitespaces from the EOF
| perl -0pe 's|^(.+)&nbsp.+$|\1|gs' \ # Strip last $PS1 output
| perl -0pe 's|^(.+)$|<span>\1</span>|gm' \ # Wrap every line with <span>
| perl -0pe 's|(.+)|<div class=\"term-container\">\1</div>|gs' \ # Wrap everything back into a <div>
| pbcopy # Copy to clipboard (MacOS only)

Wrapping it up

Myself I want to have two aliases in my ~/.zshrc

# ASCII Cinema
alias recout="cat /tmp/rec_output.txt | terminal-to-html --preview \
| perl -0pe 's:\[J([1|2]?)::g' \
| perl -0pe 's|.+?<div.+?>(.+?)<\/div>.+|\1|sg' \
| perl -0pe 's:\s*(.+):\1:gs' \
| perl -0pe 'chomp' \
| perl -0pe 's|^(.+)&nbsp.+$|\1|gs' \
| perl -0pe 's|^(.+)$|<span>\1</span>|gm' \
| perl -0pe 's|(.+)|<div class=\"term-container\">\1</div>|gs' \
| pbcopy"

alias rec='rm -f /tmp/rec_output.txt && asciinema rec --raw /tmp/rec_output.txt && recout && echo "Screen recording copied to HTML"'

Now to copy output from my terminal I just type rec, do what I need to do followed by ctrl+d . The result is HTML code in my clipboard which is ready to paste to an article.

~/demo-time! curl -s http://wttr.in/shanghai | head -n 7 Weather report: Shanghai, China   .-. Light Rain ( ). 24..27 °C (___(__) 7 km/h ‘ ‘ ‘ ‘ 6 km ‘ ‘ ‘ ‘ 0.1 mm

Demo

Code highlighting

You can also use the same technique to post code snippets from different programming languages using pygmentize.

~/GIT/aoc_py/2018 master* pygmentize -l ruby -O style=monokai day_01.rb F = File.read("#{__dir__}/data/day1.txt").lines.map(&:strip).map(&:to_i)   puts "Ans 1: %s" % F.reduce(:+)   puts "Ans 2: %s" % (0..Float::INFINITY).lazy.each_with_object([]).map{ |i, gen| l = gen.last v = (gen.to_a.last || 0) + F[i % F.length] gen << v   v if l.nil? || (not gen.take(gen.size-2).include? l) }.take_while{ |m| not m.nil? }.force.last