de[v|b]log

ShellScript, Coffee, iOS/OSX Dev
Origin: Himajinworks.
About.

内容

表題の通り。
これまでWordpressを利用していたが、以前からやりたかったので Jekyll へ移行した。

移行段階

移行をするにあたり、データを取り出していかないといけない。そこで以下の流れで移行を行った。
jekyll-import があるのに気づくのが遅かったのは言うまでもない。

大雑把に書くと、以下の流れで移行を行った。

  1. Wordpressから記事とAssetをエクスポートする
  2. エクスポートされた記事(XML)から記事やメタ情報を取り出す
  3. Jekyllで扱える形式に変換する & リンクを書き直す
  4. コードブロック記法を書き直す
  5. 後処理

こんな感じ。

Wordpressから記事とAssetをエクスポートする

この段階ではWordpress自体のエクスポート機能と、バックアップ用に利用していたプラグイン BackWPup を利用した。

Wordpress自体のエクスポート機能

ここでは記事、アップロードしたリソース情報、全てマージしたXML等を選択して出力できる。
今回は記事情報が欲しかったため、記事のXMLを取得した。尚、後述する BackWPup でも記事情報等は取得可能だが、 mysqldump でダンプされる形式(だったはず)のSQLをそのまま扱うよりXMLをパースしたほうが簡単かと思い、記事のXML情報を取得した。

エクスポートされた記事(XML)から記事やメタ情報を取り出す

今回はXMLを扱うため、 xmllint を使って処理した。
このタイミングではXML要素内にある item 要素を見ていけばよい。一度XMLからエクスポートしてその後形式変換する目的であったため、 xmllint 経由で取得できたデータをプレーンテキストに吐き出すことにした。

以下はその際利用したスクリプトである。
尚、その場でしか利用する気がなかったため汚いことは承知している。

まずは ./script.sh exported.xml というようにしてエクスポートした記事のXMLファイルを渡すことで、同レベルに位置する exported ディレクトリに

  • ${記事公開時間 (UNIX Time)}_title.txt
  • ${記事公開時間 (UNIX Time)}_tags.txt
  • ${記事公開時間 (UNIX Time)}_content.txt

を出力する。また同時に ${記事公開時間 (UNIX Time)} を連ねた index.txt を作成する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#! /usr/bin/env zsh

filename=$1

if [[ ! -e ${filename} ]]
then
  echo "no file found for ${filename}" 1>&2
  return 1
fi

result_directory='./exported'
if [[ ! -d ${result_directory} ]]
then
  mkdir -p ${result_directory}
fi

item_index_path=${result_directory}/index.txt

number_of_items=$(xmllint --xpath 'count(//item)' ${filename})
seq 1 ${number_of_items} | while read id
do
  # Take pubdate for id
  pubdate_str=$(cat ${filename} | \
    sed 's/content:encoded/content/g' | \
    xmllint --xpath "//item[${id}]/pubDate/text()" - | \
    sed 's@^<!\[CDATA\[@@;s@\]\]>$@@' | \
    nkf --numchar-input)

  pubdate_unixtime=$(gdate +%s --date "${pubdate_str}")
  echo ${pubdate_unixtime} >> ${item_index_path}

  export_path="${result_directory}/${pubdate_unixtime}"

  # Parse title
  cat ${filename} | \
    sed 's/content:encoded/content/g' | \
    xmllint --xpath "//item[${id}]/title/text()" - | \
    sed 's@^<!\[CDATA\[@@;s@\]\]>$@@' | \
    nkf --numchar-input > ${export_path}_title.txt

  # Parse tags
  number_of_tags=$(xmllint --xpath "count(//item[${id}]/category)" ${filename})
  typeset -a tags=()
  seq 1 ${number_of_tags} | while read tag_id
  do
    cat ${filename} | \
      sed 's/content:encoded/content/g' | \
      xmllint --xpath "//item[${id}]/category[${tag_id}]/text()" - | \
      sed 's@^<!\[CDATA\[@@;s@\]\]>$@@' | \
      nkf --numchar-input >> ${export_path}_tags.txt
  done

  # Parse content
  cat ${filename} | \
    sed 's/content:encoded/content/g' | \
    xmllint --xpath "//item[${id}]/content/text()" - | \
    sed 's@^<!\[CDATA\[@@;s@\]\]>$@@' | \
    nkf --numchar-input > ${export_path}_content.txt
done

Jekyllで扱える形式に変換する & リンクを書き直す

次にJekyllで読める記事形式にしていくため、以下のスクリプトを書いた。
またAsset類に対するURLの書き換えもここで行う。
以下のスクリプトでは、引数としてさっきのスクリプトで出力したディレクトリを与え、同レベルの jekyll_post に対してJekyllで利用可能なの形式のファイルを出力する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#! /usr/bin/env zsh

export_item_path=$1
if [[ ! -d ${export_item_path} ]]
then
  echo "invalid path" 1>&2
  return 1
fi

export_destination='./jekyll_post'

cat ${export_item_path}/index.txt | while read id
do
  export_filepath="${export_destination}/$(gdate '+%Y-%m-%d' --date "@${id}")-${id}.markdown"

  echo '---' > ${export_filepath}

  echo 'layout: post' >> ${export_filepath}
  echo 'title: "'$(cat ${export_item_path}/${id}_title.txt)'"' >> ${export_filepath}
  echo 'date: '$(gdate '+%Y-%m-%d %H:%M:%S %z' --date "@${id}") >> ${export_filepath}
  echo 'categories: '$(cat ${export_item_path}/${id}_tags.txt | tr '\n' ' ') >> ${export_filepath}

  echo '---' >> ${export_filepath}

  cat ${export_item_path}/${id}_content.txt | \
    ruby -r cgi -pe '$_=CGI.unescapeHTML($_)' | \
    gsed -e 's/\[code lang=\([a-z0-9]*\)\]/{% highlight \1 linenos %}/g' \
         -e 's/\[code\]/{% highlight plain linenos %}/g' \
         -e 's/\[\/code\]/{% endhighlight %}/g' \
         -e 's@-[0-9]\+x[0-9]\+.\([a-z]\+\)@.\1@g' \
         -e 's@http://blog.himajinworks.net/wp-content/uploads@{{ site.baseurl }}/assets/images/post@g' >> ${export_filepath}

  egrep '/assets/images/' ${export_destination}/* | \
    cut -d ':' -f 1 | \
    sort | uniq | \
    xargs -I{} gsed -i 's@width="[^"]\+" @@g;s@height="[^"]\+" @@g;s@class="[^"]\+" @@g;' {}
done

ざっとこんな感じになる。

1
2
3
4
5
6
7
---
layout: post
title: title
date: 20XX-XX-XX XX:XX:XX +0900
categories: tag1 tag2
---
This is content.

このような出力になることを期待している。
尚、後述するが、上記だけではコードブロック記法の置換が不十分であった。

コードブロック記法を書き直す

上記だけではコードブロックの書き換えが不十分であったため、インタラクティブシェルから以下を叩いた。しんどい。

find . -iname '*.markdown' | \
  xargs -I{} gsed -i \
    -e 's/\[bash\]/{% highlight bash linenos %}/g;s/\[\/bash\]/{% endhighlight %}/g' \
    -e 's/\[ruby\]/{% highlight ruby linenos %}/g;s/\[\/ruby\]/{% endhighlight %}/g' \
    -e 's/\[text\]/{% highlight text linenos %}/g;s/\[\/text\]/{% endhighlight %}/g' \
    -e 's/\[shell\]/{% highlight bash linenos %}/g;s/\[\/shell\]/{% endhighlight %}/g' \
    -e 's/\[c\]/{% highlight c linenos %}/g;s/\[\/c\]/{% endhighlight %}/g' \
    -e 's/\[plain\]/{% highlight text linenos %}/g;s/\[\/plain\]/{% endhighlight %}/g' \
    -e 's/\[obj-c\]/{% highlight objc linenos %}/g;s/\[\/obj-c\]/{% endhighlight %}/g' \
    -e 's/\[objc\]/{% highlight objc linenos %}/g;s/\[\/objc\]/{% endhighlight %}/g' \
    -e 's/\[js\]/{% highlight js linenos %}/g;s/\[\/js\]/{% endhighlight %}/g' \
    -e 's/\[cpp\]/{% highlight cpp linenos %}/g;s/\[\/cpp\]/{% endhighlight %}/g' \
    -e 's/\[python\]/{% highlight python linenos %}/g;s/\[\/python\]/{% endhighlight %}/g' {}

将来的に再度使うのならば配列に入れて回すところだが、今回は一度きりだったため試しながらでこの結果になった。

後処理

あとはBackWPupにてバックアップしたAsset類を移動し、シンタックスエラー等があったら手動修正で対応した。

その他

Jekyllのテンプレート等を作る必要があったためざっくりつくった。
またハイライト等の扱いに関しては各種サイトを参照した。

あとがき

移行に半日かかってしまった。