Markdownの記事作成を半自動化するスクリプト

このブログはGatsby Starter Blogをベースにしているため、記事はMarkdown(MDX)形式で書き、 そのファイルをGitリポジトリで管理している。この手法はGatsbyに限らないが、 記事を書くたびにいちいちディレクトリを作ったりファイルを作ったりするのは非常に面倒臭いしミスも起こりやすい。

なので、それを少しでも楽にするため記事作成用のスクリプトを作成した。

前提条件

このブログは過去の記事にも書いた通り、 以下のようなルールで記事を整理している。

  1. プロジェクト配下のcontent/blog以下に配置
  2. 年毎に下二桁の西暦ディレクトリを作成
  3. 2のディレクトリ内に連番3桁__記事名(ハイフン区切り)のディレクトリを作成
  4. 3配下にMarkdown(MDX)ファイルや画像ファイル等を作成

まとめると、以下のような感じになる。

content/blog
- 20
  - 001__hello-world
    - index.md
  - 002__keep-writing-articles
    - index.md
  - ...
- 21
  - 001__gatsby-starter-blog-web-fonts
    - index.md
    - sample-image.png
  - ...

このルールに従って記事を作れるようにした。

作成したスクリプト

完成したスクリプトは、 「スクリプト本体」と「記事テンプレート」の2つを用意し、 それぞれをプロジェクト直下のbinディレクトリに配置した。

作成にあたり、以下記事を参考にさせていただいた。感謝。
Gatsby製のマークダウンブログで記事ファイルをテンプレートから自動生成する

本体

bin/mkmd.sh
#!/bin/bash

TEMPLATE_FILE_NAME="template.mdx"
ARTICLE_FILE_NAME="index.mdx"
ARTICLE_BASE_DIR="content/blog"
PREFIX_DIGIT=3
SEPARATOR="__"
CURRENT_YEAR_DIR=$(date "+%y")
CURRENT_TIME=$(date +"%Y-%m-%dT%T+09:00")

ROOT_DIR="$(pwd)"
BIN_DIR="$(cd $(dirname $0); pwd)"
TEMPLATE_FILE_PATH="${BIN_DIR}/${TEMPLATE_FILE_NAME}"

cd ${ARTICLE_BASE_DIR}
if [ $? -ne 0 ]; then
  echo "記事ディレクトリが存在しないため終了します。"
  echo "プロジェクトのルートディレクトリで実行して下さい。"
  exit 1
fi

if [ ! -e "${TEMPLATE_FILE_PATH}" ]; then
  echo "テンプレートファイルが存在しないため終了します。"
  echo "当スクリプトと同じパスに${TEMPLATE_FILE_NAME}を作成して下さい。"
  exit 1
fi

mkdir -p ${CURRENT_YEAR_DIR}
cd ${CURRENT_YEAR_DIR}

LATEST_INDEX=$(ls -r | grep -E [0-9]{${PREFIX_DIGIT}} | head -n 1 | sed -r "s/^0*//; s/^(.+)${SEPARATOR}.+$/\1/")
NEW_INDEX=$(printf "%0${PREFIX_DIGIT}d" $(($LATEST_INDEX + 1)))

while :
do
  read -p "記事URL >> " ARTICLE_URL_ORG
  if [ -n "${ARTICLE_URL_ORG}" ]; then break; fi
done

while :
do
  read -p "記事タイトル >> " ARTICLE_TITLE
  if [ -n "${ARTICLE_TITLE}" ]; then break; fi
done

ARTICLE_URL=$(echo "${ARTICLE_URL_ORG}" | sed "s/ /-/g" | sed "s/[^A-Za-z0-9._-]//g")

mkdir "${NEW_INDEX}${SEPARATOR}${ARTICLE_URL}" && cd $_
cp "${TEMPLATE_FILE_PATH}" "${ARTICLE_FILE_NAME}"

function replace_template() {
  sed -i "" "s/$1/$(echo $2 | sed 's/\//\\\//g')/g" "${ARTICLE_FILE_NAME}"
}

replace_template "@TITLE" "${ARTICLE_TITLE}"
replace_template "@DATE" "${CURRENT_TIME}"

echo "記事を作成しました。"
pwd

記事テンプレート

bin/template.mdx
---
title: @TITLE
date: @DATE
---

動作の説明

設定値(3〜13行目)

bin/mkmd.sh
TEMPLATE_FILE_NAME="template.mdx"
ARTICLE_FILE_NAME="index.mdx"
ARTICLE_BASE_DIR="content/blog"
PREFIX_DIGIT=3
SEPARATOR="__"
CURRENT_YEAR_DIR=$(date "+%y")
CURRENT_TIME=$(date +"%Y-%m-%dT%T+09:00")

ROOT_DIR="$(pwd)"
BIN_DIR="$(cd $(dirname $0); pwd)"
TEMPLATE_FILE_PATH="${BIN_DIR}/${TEMPLATE_FILE_NAME}"

3〜9行目は記事作成に必要な値を設定しているだけ。上から順に以下の通り。

  • TEMPLATE_FILE_NAME: 記事テンプレートのファイル名。
  • ARTICLE_FILE_NAME: 作成される記事のファイル名。
  • ARTICLE_BASE_DIR: 記事ファイルを格納するルートディレクトリ。
  • PREFIX_DIGIT: 記事の連番の0埋め桁数。
  • SEPARATOR: 連番と記事URLのセパレータ文字列。
  • CURRENT_YEAR_DIR: 記事整理用の年ディレクトリ。西暦の下2桁。
  • CURRENT_TIME: 記事の作成日に使用する現在日時。
    いわゆる国際基準に則って、2021-07-18T10:37:06+09:00のような感じで作る。

その下は実行ディレクトリとかを取ってるだけなので省略。

設定値のチェック(15〜26行目)

bin/mkmd.sh
cd ${ARTICLE_BASE_DIR}
if [ $? -ne 0 ]; then
  echo "記事ディレクトリが存在しないため終了します。"
  echo "プロジェクトのルートディレクトリで実行して下さい。"
  exit 1
fi

if [ ! -e "${TEMPLATE_FILE_PATH}" ]; then
  echo "テンプレートファイルが存在しないため終了します。"
  echo "当スクリプトと同じパスに${TEMPLATE_FILE_NAME}を作成して下さい。"
  exit 1
fi

記事ファイルを格納するディレクトリと、 記事テンプレートファイルがそれぞれ存在するかをチェック。無ければ終了。見ての通り。

連番を取得(28〜32行目)

bin/mkmd.sh
mkdir -p ${CURRENT_YEAR_DIR}
cd ${CURRENT_YEAR_DIR}

LATEST_INDEX=$(ls -r | grep -E [0-9]{${PREFIX_DIGIT}} | head -n 1 | sed -r "s/^0*//; s/^(.+)${SEPARATOR}.+$/\1/")
NEW_INDEX=$(printf "%0${PREFIX_DIGIT}d" $(($LATEST_INDEX + 1)))

前半で西暦ディレクトリが無ければ作成して移動。

後半は西暦ディレクトリ内を調べて、最新記事の連番を取得(LATEST_INDEX)。 そんで今回作ろうとしている記事の連番を取得(NEW_INDEX)。

連番を取得するロジックをもう少し書くと、以下のような感じになっている。

  1. 西暦ディレクトリ内の中身(ディレクトリ)を降順で表示する。
  2. その中から先頭が数字3桁のものだけ抽出して先頭だけを取り出す。
  3. 取り出したディレクトリ名から、先頭の0を取り除く。
  4. セパレータ文字列(__)以降を切り捨てる。

これでとりあえず上手くいった。

記事URLとタイトルの入力と整形(34〜46行目)

bin/mkmd.sh
while :
do
  read -p "記事URL >> " ARTICLE_URL_ORG
  if [ -n "${ARTICLE_URL_ORG}" ]; then break; fi
done

while :
do
  read -p "記事タイトル >> " ARTICLE_TITLE
  if [ -n "${ARTICLE_TITLE}" ]; then break; fi
done

ARTICLE_URL=$(echo "${ARTICLE_URL_ORG}" | sed "s/ /-/g" | sed "s/[^A-Za-z0-9._-]//g")

2つのwhile文で記事URL(≒記事ディレクトリ名)と記事のタイトルを入力させる。 未入力でEnter押した場合は入力状態を繰り返すようにした。 ただ、URLはともかく、記事タイトルの方は空欄OKにしてもいい気もする。

あと、記事URLの入力の方は単語の区切りをハイフンにする必要があって入力が面倒臭いので、 最後の行で「スペースをハイフンに変換する」処理を追加する。 仕上げにURLに使えない文字列を全て除去する。

記事ディレクトリ・ファイルの作成(48〜59行目)

bin/mkmd.sh
mkdir "${NEW_INDEX}${SEPARATOR}${ARTICLE_URL}" && cd $_
cp "${TEMPLATE_FILE_PATH}" "${ARTICLE_FILE_NAME}"

function replace_template() {
  sed -i "" "s/$1/$(echo $2 | sed 's/\//\\\//g')/g" "${ARTICLE_FILE_NAME}"
}

replace_template "@TITLE" "${ARTICLE_TITLE}"
replace_template "@DATE" "${CURRENT_TIME}"

echo "記事を作成しました。"
pwd

取得した連番と入力したURLを元に記事ディレクトリを作る。 その中にテンプレートファイルからコピーして記事に使用するMarkdownファイルをコピー。

コピーしたMarkdownファイル内の所定の文字列を、それぞれ置換。

  • @TITLE: 入力された記事タイトル
  • @DATE: 最初に取得しておいた記事の作成日時

これで記事用のMarkdownファイルとディレクトリが作られる。

まとめ

テンプレートを見ての通り、出来上がるのは非常に簡素なMarkdownファイル。

もちろん、もっと色んな処理を加えて完成度の高い記事ファイルを作ることもできるだろうけど、 記事を書く前に考えていたことって結構書いてるうちに変わったりするし、最低限に留めた。

雑なノリでぽんと作れる方が気楽で取り回しもしやすいので、 今の自分のスタイル的にはこのぐらいの温度感で十分かなと思う。


止まらない実行を目指すITエンジニア。

© 2020-2022, Ryz