Using the sed Command in a Bash Script to Update Hugo Front Matter

Published: 2022 February 11

system administration Linux Bash shell command shell script software development web development static site generator Hugo regular expressions

Background

Hugo used to have an undraft command to quickly alter front matter before publishing, but it was removed.

The command would update “the content’s draft status from ‘True’ to ‘False’ and… the date to the current date and time”.

That’s useful functionality, so we’ll replicate it with a Bash script.

For this project we’ll use sed, which is one of the basic programs that comes with most distributions of Linux and it’s useful for (among other things) replacing segments of text.

Updating the draft status field

Let’s say we have a new-hugo-post.md ready to publish that we want to undraft.

In a Bash console we can enter the following command to first update the draft status:

sed -i '1,10 s/^draft: .*/draft: false/' new-hugo-post.md

Let’s break down the elements of that command.

sed runs the sed program.

-i invoke the “in-place” option to modify the existing file in-place.

'...' - that is, the single quotes and the text within them - contain the instructions for what text sed is supposed to alter.

new-hugo-post.md is the name of the file we want to modify.

Within the '...' command are two important sub-components.

1,10 tells sed to only search and alter the first 10 lines of the file. Since we’ll be working with regular expressions we’ll want to limit the scope of the script’s work as much as possible to avoid any unintentional side effects. And because we are only altering the front matter of the articles the relevant fields should likely always be in those first 10 lines and it’s very unlikely that anything else containing the strings we’re looking for will be in those lines.

s/.../.../ tells sed to find text between the first and second forward slash and replace it with the text between the second and third forward slash.

^draft: .* tells sed to find lines of text that begin with “draft: " followed by any other characters, matching to the end of the line.

draft: false tells sed to replace any matched lines with the literal string “draft: false”.

In summary, this command searches the first ten lines of the file new-hugo-post.md looking for lines of text that begin with “draft: " followed by any other characters, matching to the end of the line, and replaces those lines with the literal string “draft: false”.

Updating the date field

Now let’s update the date field.

We can get the current date and time using the date command:

date

But Hugo expects the date to be formatted in a particular way. We can achieve that with:

date '+date: %Y-%m-%dT%T%:z'

And we can assign that result as a variable that sed can reference with this command:

newdate=$(date '+date: %Y-%m-%dT%T%:z')

Now, we can use the following command to update the article to the current date and time:

sed -i "1,10 s/^date: .*/$newdate/" new-hugo-post.md

Most of the elements of this command are the same as those for updating the draft status field, but there are a few differences to consider.

"..." - that is, the double quotes and the text within them - contain the instructions for what text sed is supposed to alter. Note that this is different than the single quotes used for the draft status change. The double quotes tells sed to expand any variables (such as $newdate) instead of interpreting them as a literal string.

s/.../.../ as before, this tells sed to find text between the first and second forward slash and replace it with the text between the second and third forward slash.

^date: .* tells sed to find lines of text that begin with “date: " followed by any other charcters, matching to the end of the line.

$newdate because this variable is part of a command contained in double quotes instead of single quotes, sed expands this variable to its assigned value and then replaces any matched lines with that value.

In summary, this command searches the first ten lines of the file new-hugo-post.md looking for lines of text that begin with “date: " followed by any other characters, matching to the end of the line, and replaces those lines with the value stored in the $newdate variable, which we assigned as the current date and time formatted to the output Hugo expects.

Putting it all together

Instead of running the above commands individually, we can assemble them into a reusable script by pasting the following into a file called hugo-undraft.sh

#!/bin/bash

sed -i '1,10 s/^draft: .*/draft: false/' $1

newdate=$(date '+date: %Y-%m-%dT%T%:z')

sed -i "1,10 s/^date: .*/$newdate/" $1

echo "$0 run for $newdate on $1"

Most of the script is comprised of the three commands we already considered, so we’ll discuss the new elements.

#!/bin/bash tells the shell to interpret this file as a bash script.

$1 is a variable that replaces the file name in both of the sed commands so that the script is reusable. The file name will be passed as a parameter to this variable when the script is run.

echo "$0 run for $newdate on $1" is included in order to have some output indicating that the script was run. It prints the name of the script (which is supplied by the $0 variable), the value of $newdate, and the name of the file that changes were applied to.

Making it an executable script

In order to make this script available to run anywhere on our system there are just a few more steps that are outlined in another post.

Running the script

Once those steps are completed you can execute this script anywhere and undraft your Hugo articles automatically.

If, for example, you want to undraft the “new-hugo-post.md” article, navigate to the directory containing that article and use the command:

hugo-undraft.sh new-hugo-post.md

Copying Directory Structure With a Bash Script - With Help From ChatGPT AI

Published: 2024 January 22

Setting Up a Raspberry Pi as a Linux Print Server with CUPS

Published: 2024 January 19

Setting Up a Raspberry Pi Zero 2 W with Ubuntu Server

Published: 2024 January 04