【シェルと遊ぼう】GithubのMarkdownの目次を作ってみよう

はじめに

githubに上げてたREADME.mdの目次リンクをミスってた。

なんてこった。

そういえば、ファイル中の見出しをgrepで拾って手で加工してた。

今後こんなことがないようにツールを探そう。

と思ったけど、暇なのでシェルスクリプトで作ってみよう。

作るもの

下のようなMarkdownファイルを使った例。

# タイトル
本

## 見出し その1 AIUEoあいうえお
### 見出し (その1-1) !"#$%&'()~=-\_
## 見出し その2 []@\\:;,.
### 見出し (その2-1) {}`*+<>?`
### 見出し (その2-2)、。・¥;:」「@<>?_+*`{}・
#### 見出し [その2-2-1]!”#$%&’()=ー^〜
##### 見出し <その2-2-2>||

文

引数には対象のMarkdownファイルのパスを指定。

> mktoc test.md
- [見出し-その1](#見出し-その1)
- [見出し-その2](#見出し-その2)

標準入力からでもOK。

> mktoc < test.md
- [見出し その1 AIUEoあいうえお](#見出し-その1-aiueoあいうえお)
- [見出し その2 []@\:;,.](#見出し-その2-)

オプション引数で2〜5階層(## 見出し##### 見出し)まで指定可能。

>  mktoc -d 5 test.md
- [見出し その1 AIUEoあいうえお](#見出し-その1-aiueoあいうえお)
    - [見出し (その1-1) !"#$%&'()~=-_](#見出し-その1-1--_)
- [見出し その2 []@\:;,.](#見出し-その2-)
    - [見出し (その2-1) {}`*+<>?`](#見出し-その2-1-)
    - [見出し (その2-2)、。・¥;:」「@<>?_+*`{}・](#見出し-その2-2_)
        - [見出し [その2-2-1]!”#$%&’()=ー^〜](#見出し-その2-2-1ー)
            - [見出し <その2-2-2>||](#見出し-その2-2-2)

コード

やってること

  • 1〜18行目 引数・オプション解析
  • 20〜25行目 見出し行(##で始まる行)を拾う
  • 26〜32行目 目次の見出し行へのリンクパスを作る
  • 33〜35行目 目次の見出し行からリンクタイトルを作る
  • 36〜42行目 見出し階層ごとのインデントを作る
  • 43〜50行目 各関数を呼び出して目次を作る
  • 52〜58行目 入力(ファイル or 標準入力)を切り替えて処理を実行

目次のリスト記号を変えたいという人は48行目のハイフンを置き換えて下さいな。

リンクパスとインデントの作成部分だけ補足しましょ。

リンクパスの作成

make_link_path(){
  tr -d '!"#$%&()~=^|{}[]`*@:+;?\<>,./\' \
  | tr -d '!”#$%&()〜=^|{}「」`*@:+;?¥<>、。・' \
  | tr -d "'’"                           \
  | tr '[A-Z ]' '[a-z-]'                \
  | sed 's/^\(.*\)$/#\1/'
}

リンクに変換する仕様がよく分からなかったので、実際に試したキーボードに刻印がある記号にだけ対応している。
ハイフンとアンダーバーは削除しなくていいっぽい。

しかし全角記号も削除しないといけないとは。他にも色々あるんだろうなぁ。

ちなみにホントはURLエンコードした方が良いかもめ。

インデントの作成

make_indent(){
  sed 's/^#\{2\}\(.*\)$/\1/'            \
  | sed 's/^\( *\)#\(#*\).*$/    \1\2/' \
  | sed 's/^\( *\)#\(#*\).*$/    \1\2/' \
  | sed 's/^\( *\)#\(#*\).*$/    \1\2/' \
  | sed 's/^\( *\).*$/\1/'
}

まず最初にデフォルト階層の見出し記号(##)を削除しておく。
そのあとで行頭の#の空白への置換を3回繰り返す。
最後に先頭の空白以外を削除している。

5階層までしか対応していないのはこの作りのせいです。

なかなかの手抜き感ですな。

あとはループ使う方法しか思い浮かばないなぁ。。。

対応する階層を増やしたい場合は、下の行を増やして、4行目のMAX_DEPTH=5も増やせばOK。

  | sed 's/^\( *\)#\(#*\).*$/    \1\2/' \

さいごに

ループ使っちゃったので、実行速度は遅め。
shの関数がxargsで指定できれば嬉しいのになー。

まぁ2000行程度の単体ファイルに対してなら実用的な速度かと。

ちなみに、ツールを探してみたらpythonで作った人もいらっしゃった。

どうやら削除対象の全角記号は下を対象にしてるっぽい。
気になる人はmake_link_path関数に追加して下さいまし。

remove_targets = u'[、。,.・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇‐/\~∥|…‥‘’“”()〔〕[]{}〈〉《》「」『』【】+-±×÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&*@§☆★○●◎◇◆□■△▲▽▼※〒→←↑↓〓∈∋⊆⊇⊂⊃∪∩∧∨¬⇒⇔∀∃∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬ʼn♯♭♪ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя─│┌┐┘└├┬┤┴┼━┃┏┓┛┗┣┳┫┻╋┠┯┨┷┿┝┰┥┸╂。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡ ㍻〝〟№㏍℡㊤㊥㊦㊧㊨㈱㈲㈹㍾㍽㍼≒≡∫∮∑√⊥∠∟⊿∵∩∪]'

そして、今回作ったものは同名リンクへの対応が抜けてるっぽい。

なんてこった。

しかし、素敵な方法がパッと思い浮かばない。

気が向いたときに対応しましょ。

確認環境

   
PC Thinkpad X1 Carbon 2nd Gen
OS FreeBSD 12.1-RELEASE-p1