Create a table without a header in reStructuredText and Markdown

Have you ever written a table without a header in Markdown? It turns out that most Markdown parsers don't support tables without headers.

When I wrote the page about my book Control Your Home with Raspberry Pi on this website, I wanted to list some specifications (title, publication date, number of pages, ...) in a table without a header. In reStructuredText, which I'm using for this website, the code for the table looks like this:

| **Title**            | Control Your Home with Raspberry Pi |
| **Author**           | Koen Vervloesem                     |
| **Publication date** | 2020-08-17                          |
| **Number of pages**  | 331                                 |
| **Price**            | € 37.50                             |
| **ISBN-13**          | 978-1-907920-94-3                   |
| **ISBN-10**          | 1-907920-94-3                       |
| **Publisher**        | Elektor International Media (EIM)   |

This is rendered by Nikola, the static site generator I'm using, as:


Control Your Home with Raspberry Pi


Koen Vervloesem

Publication date


Number of pages



€ 37.50






Elektor International Media (EIM)

This looks fine as a simple table without a header.

Now I wanted to do the same in Markdown in the corresponding GitHub repository with code examples. I could have written the README as a reStructuredText file, but I already created a out of habit, so I tried to create the same table without header in Markdown. But apparently GitHub-flavoured Markdown and many other Markdown flavours don't support tables without headers.

That StackOverflow post linked above shows a hack that seems to work in many Markdown parsers, including in GitHub:

|    <!-- -->          |        <!-- -->                     |
| **Title**            | Control Your Home with Raspberry Pi |
| **Author**           | Koen Vervloesem                     |
| **Publication date** | 2020-08-17                          |
| **Number of pages**  | 331                                 |
| **Price**            | € 37.50                             |
| **ISBN-13**          | 978-1-907920-94-3                   |
| **ISBN-10**          | 1-907920-94-3                       |
| **Publisher**        | Elektor International Media (EIM)   |

This adds HTML comment blocks in the header cells, which essentially adds an empty header row to the table. Unfortunately in GitHub the result looks a bit odd, with that compressed empty header row:


This is literally an ugly hack. Of course I can just create an HTML table without a header in the Markdown file, as shown in one of the StackOverflow answers, but that defeats the purpose of using a more human-centered markup language. So now I have converted the README file from Markdown to reStructuredText. The result is rendered by GitHub as:


Just like I wanted. It's these small quibbles with Markdown all the time that strenghten my preference for reStructuredText as a markup language.

Heavy reading


I knew I had some heavy reading on my bookshelf, but I was just surprised by this view in my home office. Coincidentally, it was one of the mathematics shelves. 1


Actually, the shelf broke by what you don't see in this picture: the stack of magazines that stood in front of the row of books and is now lying on the floor all around the room.

Automating society

Earlier this year, I wrote two articles for AlgorithmWatch:

Both articles were part of AlgorithmWatch's upcoming report Automating Society 2020, which investigates the applications of automated decision-making in the public sector in various European countries.

The full report will be published this fall, but the abridged versions of both stories that will come in the report have now been published on the website Ethics of Algorithms of the Bertelsmann Stiftung:

Especially the Dutch story about SyRI, an algorithm cross-referencing personal data from citizens in various databases, makes it clear that algorithmic fraud detection has a lot of pitfalls: the way it was used in the Netherlands there was no transparency (the exact algorithm or risk model was never disclosed), it exacerbated biases and discrimination, and it was used for purposes it wasn't designed for.

The best argument against such systems is actually quite simple: you don't need them. As Ronald Huissen from the Platform Bescherming Burgerrechten said, the government doesn't need this kind of mass surveillance to prevent fraud:

The government already has information about who owns which house, so it could check this before granting the person a rental allowance. For all big fraud scandals in social security we have seen in the past decades it became clear afterwards that they could have been prevented with simple checks beforehand. That happens far too little. It is tempting to look for solutions in secret algorithms analyzing big data sets, but often the solution is far simpler.

It's an issue I see all too often: there's a problem (social fraud, fiscal fraud, crime, a virus outbreak, ...) and the government thinks it has to solve this with a big centralized database, massive surveillance and some technological voodoo (big data, AI, an app, or the next big thing). The solution is often much simpler: everyone just has to do their job instead of outsourcing it to technology.

Installing packages in Ubuntu's live environment

Recently I wanted to boot Ubuntu's live CD and install the OpenSSH server in this live environment so I could access it from another computer.

This is no problem, but I'm using a Belgian keyboard layout. Although I have learned writing qwerty on an azerty keyboard, I thought it would be easier to change the keyboard layout.

I never remember the right syntax for setxkbmap, so I just started the graphical installer and went through the first steps until I had changed the keyboard layout.

Then I opened a terminal, set a password for the ubuntu user and installed the OpenSSH server:

ubuntu@ubuntu:~$ passwd
ubuntu@ubuntu:~$ sudo apt update
ubuntu@ubuntu:~$ sudo apt install --yes openssh-server

Uh-oh, the installation finished with an error. I investigated with sudo journalctl -xe:

Jul 13 08:48:51 ubuntu sshd[9072]: /etc/ssh/sshd_config: No such file or directory
Jul 13 08:48:51 ubuntu systemd[1]: ssh.service: Control process exited, code=exited, status=1/FAILURE

I was puzzled: why would this basic configuration file not be created? OK, I thought, let's just create the file and restart the OpenSSH server:

ubuntu@ubuntu:~$ sudo touch /etc/ssh/sshd_config
ubuntu@ubuntu:~$ sudo systemctl restart sshd

But then the logs showed another error:

Jul 13 08:51:45 ubuntu sshd[9144]: Privilege separation user sshd does not exist
Jul 13 08:51:45 ubuntu systemd[1]: ssh.service: Control process exited, code=exited, status=255/EXCEPTION

Wait a minute, this really can't be right. If the sshd user isn't even created, I did something seriously wrong. So I took a step back and looked at the original error message, which I brushed off earlier. I now saw it complained with "Could not open lock file /var/lib/apt/lists/lock". And then I remembered: oh, I left the installer window open! I closed the window and then ran:

ubuntu@ubuntu:~$ sudo apt --fix-broken install

This fixed the installation of OpenSSH server, after which I could access the live environment from another computer.

Moral #1 of the story

Don't let the installer window or other package management windows open while you're running package management tasks in the terminal.

Moral #2 of the story

Always read error messages carefully.

Fixing a failed upgrade to Ubuntu 20.04 LTS in recovery mode

My main laptop has been running Ubuntu 19.10 (Eoan Ermine). I deliberately postponed the upgrade to Ubuntu 20.04 LTS (Focal Fossa) because I had a very busy period writing a book and studying for evening classes. I just couldn't afford the time to fix potential upgrade issues, my laptop should "just work".

But now time is running out: Ubuntu 19.10 will reach end of life status on July 17th, 2020. Because I'm having a 'vacation' week now, I could spend some time if the upgrade fails.

And failing it did...

I'm not sure what went wrong, but after all packages had been downloaded and when the upgrade process was in the middle of applying the package upgrades, the screen became grey and showed the message "Something has gone wrong. Please logout and try again." I had to restart Ubuntu and it even didn't reach the login screen.

I first tried removing quiet splash from the boot entry, but that didn't make me any wiser. So off to recovery mode then:

  • Hold the Shift key while booting the PC.

  • In the GRUB boot menu that appears, choose the advanced options and then recovery mode.

  • In the recovery menu that appears, enable networking first and then choose the option to open a root shell.

Because the installation has been aborted, I tried fixing a broken install:

# apt --fix-broken install

To my surprise this "just worked", so it seems the system wasn't broken that badly.

To be sure I didn't miss any package configuration, I configured all unpacked but not yet configured packages:

# dpkg --configure -a

This returned silently, so no issues there.

Then I continued the upgrade:

# apt upgrade

This all went smoothly. So after the upgrade succeeded, I had Ubuntu 20.04 on my system:

# lsb_release -a
No LSB modules are available.
Distributor ID:   Ubuntu
Description:  Ubuntu 20.04 LTS
Release:  20.04
Codename: focal

I rebooted:

# reboot

And then I could login again and was welcomed by this stylish desktop background:



If you don't remember exactly what you did in recovery mode to fix your upgrade and you have already rebooted, just run sudo su and then history. It will show you the commands you ran as root.

New blog

I'm no stranger to blogging. There was a time when I wrote almost daily on two blogs (in Dutch): on "De conceptuele ingenieur" (no longer online) I wrote about philosophy and science, and on "QED" (archived at I wrote about mathematics and computers. But more than ten years ago I stopped blogging, because I lacked the time.

For a couple of years I have been thinking I should start over. Now's the time. But this time not with a specific theme or a split personality. On this blog I will write about everything that interests me.

That's it for now. You'll see what comes next.


This blog is bilingual. Some articles are only published in Dutch, others only in English, others in both languages. In the latter case you will see a link at the top of the article named Also available in: Nederlands. If you don't want to miss articles written in Dutch, make sure to click on Nederlands at the top of the web site and/or subscribe to the RSS feed of both languages.