Inserting slugs with bash & sed

Let’s use bash and sed to add a new line of dynamic content near the top of a bunch of files.

Motivation

I recently switched this site from using Jeykll to using Hugo. To do so without breaking my old site’s URLs, I found that I needed to insert a line of YAML front matter at the top of each post that looks something like this:

slug: "sed_insert"

Jekyll used the filenames as the URL slug, so for each post, I needed to insert the filename as the slug on a new line towards the top of the file:

---
title: "Inserting lines with sed"
date: 2017-09-09T13:52:28-07:00
slug: "sed_insert"
---

Getting the Slug

Because each filename matches the pattern *.markdown, I can use one of the many bash string manipulation operators to lop off the “.markdown” suffix. The ${string%substring} pattern will remove the shortest trailing match of substring from string:

for f in *.markdown; do echo "${f%.markdown}"; done
connection-refused
debian-packages-in-docker
django-encryption-at-rest
docker-arm7-chip
nsenter_network_namespace
openvpn-with-docker
port-socket
stroke-width-transform
syn-backlog
unicode-python

Constructing the YAML

We can tinker with the loop a bit to add the necessary yaml sugar. The wonky quoting is says: echo the thing wrapped in double quotes, interrupting the double quotes on occasion to explicitly print a double-quote-symbol:

#!/bin/bash
for f in *.markdown
do
    echo "slug: "'"'"${f%.markdown}"'"'
done

# prints
slug: "connection-refused"
slug: "debian-packages-in-docker"
slug: "django-encryption-at-rest"
slug: "docker-arm7-chip"
slug: "nsenter_network_namespace"
slug: "openvpn-with-docker"
slug: "port-socket"
slug: "stroke-width-transform"
slug: "syn-backlog"
slug: "unicode-python"

Inserting with sed

On my macbook, sed is really old. So I’ve installed GNU sed via brew install gnu-sed, which installs it as gsed. You should be able to do something similar on a linux machine with sed.

The pattern 4 i is interpreted by sed as: We want to tell sed to do the following: on the fourth line (one-indexed), insert the following content. Fortunately for us, we can use the 4 ifoo pattern to insert the word foo on line four! Let’s tie everything together to insert the correct yaml:

#!/bin/bash
for f in *.markdown
do
    gsed -i -e "4 islug: "'"'"${f%.markdown}"'"' ${f}
done

Note that I’m using the -i flag to do in-place modification of each file, rather than printing the modified contents to stdout, but dropping this flag will let you test things out without affecting file contents.