f

2016-10-23

How to check if command option is enabled in POSIX shell script

シェルスクリプトで,コマンドのオプションが有効かどうかの判定方法を記す。

Introduction

複数のマシンを使っていると,マシンによってコマンドオプションが異なることがある。これは,主に以下2点の理由が原因だ。

  1. コマンドのバージョンの違い
  2. 開発元の違い

1のバージョンによるオプションの違いの例としては,GNU grepコマンドがある。GNU grepでは,version 2.5.2から--exclude-dirオプションが追加された。このオプションを使うことで,検索対象外ディレクトリを設定できる。しかし,version 2.5.2以前の古いマシンのGNU grepではこのオプションは存在しない。

2の開発元の違いの例としては,lsコマンドがある。lsコマンドの表示結果に色を付けるオプションが存在している。このオプション名がLinuxたと--colorだが,Macだと-Gである。

これらのコマンドのオプションの有無による問題は,POSIXで未定義のベンダーの独自拡張オプションを使っていることが原因だ。これらのオプションをそもそも使わなければいいという考え方もある。しかし,利便性を考えるならこれらのオプションが使えるなら使いたい。例えば,grepの--exclude-dirオプションで,.gitや.svnなど常に検索対象外にしたいファイルを指定して予めaliasで指定しておいたがほうが便利だ。lsの表示結果に色をつけるオプションについても同様だ。

POSIX原理主義的に考えるならば,オプションが存在するときだけ使うようにきちんどガードすれば問題ないだろう。オプションが存在しなくてもエラーを出さずに処理は通常どおり行い,可用性を維持すればいい。

Method

オプションの存在の有無の判定方法には2通りの方法がある。

  1. コマンドのバージョンから判定
  2. コマンドのオプションの存在の判定

1. のコマンドのバージョンから判定する方法では,--versionオプションにより表示されるバージョン番号からオプションが存在するかどうかを判断する。この方法では,以下2点の欠点があるので不利だ。

  • オプションがサポートされるバージョン番号の把握が必要
  • 開発元の違いによる判定が煩雑

2.の方法では,--helpオプションにより表示されるオプション一覧からオプションの有無を判断する。この方法では,実際にオプションが存在するかどうかを判定するので確実だ。

なお,--versionオプションと--helpオプションは,GNU Coding Standardsで規定されており,存在する可能性が高い。

参考:4.7 Standards for Command Line Interfaces - GNU Coding Standards

しかし,--help--versionオプションはPOSIXで規定されていないので全コマンドに存在するとは限らない。そのため,そのままではPOSIX原理主義に反してしまう。そこで,これらのオプションが存在しないことも考慮して,2>&1により標準エラー出力も含めて判定条件として扱う。これにより,POSIXの範囲内の動作だけでオプション有無の判定ができる。

Coding

それでは,実際にオプションの判定方法を説明しよう。

まず,コマンドのオプションとして想定されるパターンを考える。そのためのコマンドのオプションの挙動としてPOSIXの以下の文書を参照する。

参考:12 Utility Conventions - The Open Group Base Specifications Issue 7, 2016 Edition

上記からオプション有無の判定で必要な項目と,それらの項目が実際に--helpでどのように表示されるかの例を以下の表にまとめた。

コマンドオプションの種類とpr --helpでの表示例
項目--helpでの表示例
ショートオプション-m
ロングオプション--merge
引数なしオプション-m
必須引数ありオプション-N, --first-line-number=NUMBER
任意引数ありオプション-S[STRING], --sep-string[=STRING]

任意引数ありオプションは,POSIXでは非推奨とされており,実装されているコマンドは少ない。prコマンドはこれらの全てのオプションが実装されているPOSIX準拠コマンドなのでとても参考になる。

--helpで表示される文字列に対して,POSIXで定義されているgrepコマンドでのマッチを用いて,オプションの有無を判定する。オプションの有無を判定する構文は以下となる。

オプションの有無の判定構文
<command> --help 2>&1 | grep -q -- '<option>[[:blank:],=[]'

以下で上記の構文の内容を解説する。

  1. コマンドのヘルプを<command> --help 2>&1 |により,--helpが存在しない場合のエラーも含めてパイプで渡す。
  2. コマンドが成功したかどうかの終了ステータスでオプションの有無を判定するので,grepの-qオプションにより,マッチしたときの余計な情報を表示させない。
  3. 引数--により,マッチに使用するオプションをgrepのオプションではなく引数として扱う。
  4. GNU prのpr --helpの表示例より,オプションの直後に続く文字は", =["の4文字に限られることがわかる。後ろの4文字を付けなければ,他のオプションの部分文字列としてマッチする可能性がある。また,Busyboxなどでは空白の代わりにタブが使われている。これらをオプションの直後に置いて'<option>[[:blank:],=[]'によりマッチングさせる。[:blank:]は空白とタブにマッチする。

毎回上記の構文を記述するのは少し長ったらしいので,関数にしてしまおう。

コマンドオプションの有無を判定するis_option_enabled関数
#!/bin/sh
# \file      is_option_enabled.sh
# \author    SENOO, Ken
# \copyright CC0

set -u

is_option_enabled()(
 CMD="$1"; OPT="$2"
 $CMD --help 2>&1 | grep -q -- "$OPT"'[[:blank:],=[]'
)

is_option_enable "$@"

## Test
# is_option_enabled ls   --test        && echo "OK" || echo "NG"  # NG
# is_option_enabled grep --exclude-dir && echo "OK" || echo "NG"  # OK

is_option_enabled関数の第1引数にコマンド,第2引数にオプションを指定して実行する。

なお,関数の最後が"$OPT"'[[:blank:],=[]'のように,$OPTとそれ以降を別の引用符で囲んでいる。"$OPT[=[, ]"としてもbashで動くのだが,zshでinvalid subscriptというエラーが表示されてしまうのでこちらを採用した。これは,zshでは"$OPT[]"のブロックで配列と誤認されてしまうからのようだ。

Conclusion

POSIX原理主義でコマンドに特定のオプションが存在するかの判定方法について説明した。コマンドの有無に比べたらマイナーで,あまり使う場面がないかもしれない。しかし,aliasの設定においては重要だと思う。実際に,grepの--exclude-dirオプションは常に指定したいので,今回説明した方法でif文でガードをかけてからaliasでgrepを再定義している。

自分のPOSIX原理主義を実践していく上で必要な情報は今後もまとめていきたい。

Solution for garbled characters on GNU Screen

GNU Screenを起動すると日本語などが文字化けするようになってしまった。この原因と解決策を記す。

初めはxtermの文字エンコーディングが原因かと思ったが,よく調べていくと違った。GNU Screenの起動中だけ文字化けしていた。そこで,GNU Screenの文字エンコーディング関係のマニュアルを確認してみた。GNU Screenのencコマンドの説明が参考になった。

— Command: encoding enc [denc]
(none)
Tell screen how to interpret the input/output. The first argument sets the encoding of the current window. Each window can emulate a different encoding. The optional second parameter overwrites the encoding of the connected terminal. It should never be needed as screen uses the locale setting to detect the encoding. There is also a way to select a terminal encoding depending on the terminal type by using the ‘KJ’ termcap entry. See Special Capabilities.

Supported encodings are eucJP, SJIS, eucKR, eucCN, Big5, GBK, KOI8-R, CP1251, UTF-8, ISO8859-2, ISO8859-3, ISO8859-4, ISO8859-5, ISO8859-6, ISO8859-7, ISO8859-8, ISO8859-9, ISO8859-10, ISO8859-15, jis.

See also ‘defencoding’, which changes the default setting of a new window.
11.11 Character Processing - Screen User's Manual

ここを参照する限り,GNU Screenは起動時のロケールを端末のロケールとして設定するようだ。

そして,GNU Screenの現在の文字エンコーディングは以下のコマンドで確認できる。

screen -Q info

参考:screen(1) - Linux manual page

実際にLANG環境変数を指定してGNU Screenを起動して,文字エンコーディングを確認すると,以下のとおりになった。

LANG= screen
screen -Q info
(1,4)/(80,41)+1024 -(+)flow G0[BBBB] 0(bash)
LANG=ja_JP-UTF-8 screen
screen -Q info
(1,4)/(80,41)+1024 -(+)flow UTF-8 0(bash)

後ろから2番目のフィールドがGNU Screenの文字エンコーディングを表している。LANG環境変数の設定をなしにすると,G0 (=ASCII)に設定されてしまっている。

問題の起きたマシンでは,LANG環境変数がどこにも設定されておらず,既定のLANG=Cが適用されてしまっていたようだ。.bashrcなどで以下のようにLANG環境変数を設定することで解決した。

export LANG=ja_JP.UTF-8

なお,同種のソフトであるtmuxでもLANG環境変数によって文字化けが発生するか確認したが,問題なかった。tmuxでは起動時のLANG環境変数には依存しないようだ。

2016-10-22

How to skip GRUB boot menu

LinuxのブートローダーであるGRUBの起動画面をスキップする方法を記す。

結論としては,/etc/default/grubのパラメーターを以下のとおりに変更すれば実現できる。

# GRUB_HIDDEN_TIMEOUT=0
GRUB_TIMEOUT=0

Introduction

PCの電源を入れると,ブートローダーと呼ばれるプログラムがメモリに読み込まれる。このブートローダーがさらに別のプログラムを呼ぶことを繰り返して最終的にOSがメモリに読み込まれPCの起動が完了する。

GNU GRUB(GRand Unified Bootloader)はこのブートローダーの自由なソフトであり,Linux OSで採用されているブートローダーだ。

PCを起動すると,以下のどのOSで起動するかのGRUBの選択画面が表示されることがある。

GRUBの起動メニュー

マシンのトラブルなどで古いバージョンのOSで起動する必要があるなど,いつもと違うOSを選択する場合はありえる。しかし,普段はOSの選択をする必要はなく,このGRUBの画面は省略したい。

そこで,このGRUBのメニューをスキップする方法を調べた。なお,Ubuntu 16.04のGRUB 2.02-beta2で動作を確認した。

設定

インターネットでざっと調べてみると,どうやら/etc/default/grubファイルにかかれている以下のパラメーターを設定を変更すれば実現できそうというのがわかった。

  • GRUB_TIMEOUT
  • GRUB_HIDDEN_TIMEOUT

ただ,ネットの情報は古かったり間違っていることがよくある。そこで,GRUBの公式マニュアルを確認した。なお,GNUのソフトはinfoコマンドでもマニュアルを確認できる。例えば,以下のコマンドで閲覧できる。

info grub

このマニュアルの「5.1 Simple configuration handling」にパラメーターの説明が書かれている。ここから,GRUB_TIMEOUTGRUB_HIDDEN_TIMEOUTの説明をまとめると以下の表のとおりとなる。

GRUBの画面の待ち時間に関する設定
変数説明
GRUB_TIMEOUTメニューが表示されてからデフォルト項目の起動までの待ち時間
GRUB_HIDDEN_TIMEOUTメニューに入るためのキー入力の受け付け時間

GRUB_HIDDEN_TIMEOUTが少しわかりにくいので補足する。PCの起動直後にF2やF8キーを押下すれば,ユーザーが自分でGRUBの画面を表示させることができる。このキーの入力受付時間がGRUB_HIDDEN_TIMEOUTと考えればよいだろう。

また,マニュアルのGRUB_HIDDEN_TIMEOUTの説明を読むとGRUB_TIMEOUTGRUB_HIDDEN_TIMEOUTのどちらを設定すればよいかはっきりする。

GRUB_HIDDEN_TIMEOUT

Wait this many seconds for a key to be pressed before displaying the menu. If no key is pressed during that time, display the menu for the number of seconds specified in GRUB_TIMEOUT before booting the default entry. We expect that most people who use GRUB_HIDDEN_TIMEOUT will want to have GRUB_TIMEOUT set to ‘0’ so that the menu is not displayed at all unless a key is pressed. Unset by default.

5.1 Simple configuration handling - GNU GRUB Manual 2.00

ここで書かれている通り,画面表示を飛ばしたければ,GRUB_TIMEOUT=0と設定すればよいことがわかる。

設定変更

設定すべき項目が分かったので,設定ファイル/etc/default/grubを修正する。

以下のコマンドで設定を変更して,GRUBの設定を更新する。

sudo sed -i 's/^\(GRUB_TIMEOUT=\)[0-9]\+/\10/'    /etc/default/grub
sudo sed -i 's/^\(GRUB_HIDDEN_TIMEOUT=.*\)/# \1/' /etc/default/grub
sudo update-grub

上記コマンドでは,以下のようにGRUB_TIMEOUTを0に設定して,GRUB_HIDDEN_TIMEOUTをコメントアウトしている。もちろんテキストエディタで編集してもよい。GRUB_HIDDEN_TIMEOUTをコメントアウトした理由は次の節で説明する。

# GRUB_HIDDEN_TIMEOUT=0
GRUB_TIMEOUT=0

これで設定は完了した。

起動が早くなったかどうか気になったので,実際に起動時間を測って確認した。

  • GRUB_TIMEOUT=0:1.25 min
  • GRUB_TIMEOUT=10で即選択:1.25 min

起動画面が表示されて即選択した場合と,起動時間が変わっていないので成功している。

GRUB_HIDDEN_TIMEOUTをコメントアウトした理由

当初はGRUB_HIDDEN_TIMEOUTはコメントアウトしていなかったのだが,update-grubの実行後に以下のメッセージが表示されてしまったからだ。

Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.

日本語訳:GRUB_HIDDEN_TIMEOUTが設定されている時に,GRUB_TIMEOUTを非0の値に設定することは,もはや対応されない。

この件について調べると,どうやらGRUB_HIDDEN_TIMEOUTの設定は廃止予定事項のようで,GRUB_HIDDEN_TIMEOUTはコメントアウトしたほうがいいらしい。

参考:grub2 - Grub update warning in Ubuntu 14.04 - Ask Ubuntu

公式マニュアルに書かれていなかったので疑問に思ってさらに調べた。使っているGRUBが2.02-beta2だけど,参照していたマニュアルが2.00だったので,マニュアルに更新があったのかもしれないと思い,最新ソースをあたった。最新ソースのマニュアルの元ファイルは以下となっている。

参考:grub.texi\docs - grub.git - GNU GRUB

この確認してみたところ,2013-11-28のこのコミットで廃止予定であることが付け加えられたようだ。リリース版としては,2.02-beta1から,この変更が入っている。

まとめと標準の/etc/default/grub

/etc/default/grubの以下の2パラメーターを変更することで,GRUBの起動が画面を省略できるようになった。

# GRUB_HIDDEN_TIMEOUT=0
GRUB_TIMEOUT=0

これでPCの起動速度が早くなったので,PC作業が少し快適になっただろう。最後に,設定を間違えてしまったときのために,標準の/etc/default/grubを掲載する。

Ubuntu 16.04日本語Remixの標準の/etc/default/grub
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

# Uncomment to disable graphical terminal (grub-pc only)
#GRUB_TERMINAL=console

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'
#GRUB_GFXMODE=640x480

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
#GRUB_DISABLE_LINUX_UUID=true

# Uncomment to disable generation of recovery mode menu entries
#GRUB_DISABLE_RECOVERY="true"

# Uncomment to get a beep at grub start
#GRUB_INIT_TUNE="480 440 1"

2016-10-10

Install Sphinx from source on Linux

Pythonで書かれたドキュメントツールであるSphinxをオフラインのLinuxでソースからインストールしたので方法を記す。今回の方法ではPython2でも3でも対応できる。

Introduction

職場のソースコードの文書化システムとしてSphinxがどんなものか試すことになり,まずはインストールする必要があった。

Pythonは3.4からパッケージマネージャーのpipが標準で付属しており,通常であればこれを使えば簡単にSphinxをインストールできる。

pip install --user sphinx

ただし,pipは不足している依存関係をWebからダウンロードしようとするので,インターネットと繋がっていなければこの手段は使えない。今回は職場のインターネットに繋がっていないマシンにインストールしてみたかったので,この方法ではだめだ。そこで,インターネットに繋がるマシンで必要な依存関係を自分でダウンロードして,インストールしたいマシンにtar.gzを配置する。そこで,tar.gzに対してpipを実行することでインストールする。

必要な依存関係はパッケージのsetup.pyrequires変数に書かれている。これをみると,Sphinxの依存関係は以下であることがわかる。

requires = [
    'six>=1.4',
    'Jinja2>=2.3',
    'Pygments>=2.0',
    'docutils>=0.11',
    'snowballstemmer>=1.1',
    'babel>=1.3,!=2.0',
    'alabaster>=0.7,<0.8',
    'imagesize',
    'requests',
]

さらに,これらの依存関係に存在するパッケージが以下に示す依存関係を必要とする。

babel
pytz
jinja
markupsafe

全部のパッケージを手動でsetup.pyからインストールするのは面倒なので,以下の手順でpipでインストールする。

  1. 最初にpipをソースからインストール
  2. 残りのSphinxの依存関係はpipでソースファイルからインストール

pipの依存関係はsetupttoolsである。これらを整理すると,以下の順番でインストールを行うこととなる。

  1. ソースのsetup.pyからpipをインストール
    1. setuptools
    2. pip
  2. Sphixの依存関係をpipでインストール
    1. pytz, markupsafe
    2. その他(six, Jinja2, Pygments, docutils, snowballstemmer, babe, alabaster, imagesize,requests)
    3. Sphinx
今回はルート権限を使いたくないので,$HOME/.local配下に全てインストールする。便宜のためLOCAL=$HOME/.localとしておく。

Download all dependencies

依存関係を手作業で全てダウンロードするのは面倒なのでwgetでのダウンロードコードを以下に掲載する。

bash
LOCAL="$HOME/.local"
GET="wget -nc"
mkdir -p "$LOCAL/src/python"
cd "$LOCAL/src/python"

## For pip
$GET https://pypi.python.org/packages/6b/dd/a7de8caeeffab76bacf56972b3f090c12e0ae6932245abbce706690a6436/setuptools-28.3.0.tar.gz
$GET https://pypi.python.org/packages/e7/a8/7556133689add8d1a54c0b14aeff0acb03c64707ce100ecd53934da1aa13/pip-8.1.2.tar.gz

## For Sphinx
$GET 'https://pypi.python.org/packages/1f/f6/e54a7aad73e35232356103771ae76306dadd8546b024c646fbe75135571c/Sphinx-1.4.8.tar.gz#md5=5ec718a4855917e149498bba91b74e67'

$GET 'https://pypi.python.org/packages/53/35/6376f58fb82ce69e2c113ca0ebe5c0f69b20f006e184bcc238a6007f4bdb/pytz-2016.7.tar.bz2#md5=8d8121d619a43cf0b38a4195de1cb8a5'
$GET 'https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz#md5=f5ab3deee4c37cd6a922fb81e730da6e'

$GET 'https://pypi.python.org/packages/b3/b2/238e2590826bfdd113244a40d9d3eb26918bd798fc187e2360a8367068db/six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55'
$GET 'https://pypi.python.org/packages/f2/2f/0b98b06a345a761bec91a079ccae392d282690c2d8272e708f4d10829e22/Jinja2-2.8.tar.gz#md5=edb51693fe22c53cee5403775c71a99e'
$GET 'https://pypi.python.org/packages/b8/67/ab177979be1c81bc99c8d0592ef22d547e70bb4c6815c383286ed5dec504/Pygments-2.1.3.tar.gz#md5=ed3fba2467c8afcda4d317e4ef2c6150'
$GET 'https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz#md5=4622263b62c5c771c03502afa3157768'
$GET 'https://pypi.python.org/packages/20/6b/d2a7cb176d4d664d94a6debf52cd8dbae1f7203c8e42426daa077051d59c/snowballstemmer-1.2.1.tar.gz#md5=643b019667a708a922172e33a99bf2fa'
$GET 'https://pypi.python.org/packages/6e/96/ba2a2462ed25ca0e651fb7b66e7080f5315f91425a07ea5b34d7c870c114/Babel-2.3.4.tar.gz#md5=afa20bc55b0e991833030129ad498f35'
$GET 'https://pypi.python.org/packages/71/c3/70da7d8ac18a4f4c502887bd2549e05745fa403e2cd9d06a8a9910a762bc/alabaster-0.7.9.tar.gz#md5=b29646a8bbe7aa52830375b7d17b5d7a'
$GET 'https://pypi.python.org/packages/53/72/6c6f1e787d9cab2cc733cf042f125abec07209a58308831c9f292504e826/imagesize-0.7.1.tar.gz#md5=976148283286a6ba5f69b0f81aef8052'
$GET 'https://pypi.python.org/packages/2e/ad/e627446492cc374c284e82381215dcd9a0a87c4f6e90e9789afefe6da0ad/requests-2.11.1.tar.gz#md5=ad5f9c47b5c5dfdb28363ad7546b0763'

Install setuptools and pip

まず,setuptoolsとpipをソースのsetup.pyからインストールする。setup.pyからインストールするときは,インストール先にPYTHONPATHに設定されているディレクトリを指定しなければエラーとなる。PEP 370によれば,Unixでは~/.local/lib/pythonX.X/site-packages(X.Xはバージョン)が既定で指定されているので,事前にこのディレクトリを用意しておけばよい。なお,この場所が気に入らなければ,PYTHONUSERBASE環境変数に値を指定することで,~/.localの部分を変更できる。

現在のPythonのバージョンをシェルスクリプトのワンライナーで変数に取得してディレクトリを作成しておく。

PYTHONVERSION=$(python -V 2>&1 | grep -E -o '[0-9]+\.[0-9]+')  # Get python version
mkdir -p ~/.local/lib/python$PYTHONVERSION/site-packages

そして,stuptoolsとpipを順番にインストールする。

## setuptools
cd $LOCAL/src/python
tar xf setuptools-*.tar.gz
cd setuptools-*
python setup.py install --prefix=$LOCAL

## pip
cd $LOCAL/src/python tar xf pip-*.tar.gz cd pip-* python setup.py install --prefix=$LOCAL

これでpipのインストールが完了した。

Install Sphinx

続いてpipを使ってSphinxに必要なパッケージをインストールしていく。babelとjinja2だけpytzとMarkUpSafeの依存 関係が必要なので,これらを先にインストールしていく。また,一個ずつpipでインストールするのは煩雑なので,Sphinxだけ最後にインストールする ようにして,for文を使ったシェルスクリプトで一括でインストールする。

cd $LOCAL/src/python
pip install --user pytz-*.tar.*
pip install --user MarkupSafe-*.tar.*

for i in *.tar*
do
[ "${i%%*Sphinx*}" ] && pip install --user "$i"
done

pip install --user Sphinx-*.tar.*

これでSphinxのインストールは完了した。

Conclusion

Sphinxをソースコードからインストールする方法を記した。必要なものは全てソースからインストールしたのでPythonのバージョンに依存せず,2でも3でも対応できた。実際には,Python2.7と3.3で動作を確認できた。

普段はpipを使っていてあまり意識しないが,Sphinxはたくさんの小さなパッケージに依存していることが分かった。1個ずつ手動でインストールするのは面倒なので,pipが便利だと思った。たぶん今回のようにオフラインの環境にインストールすることはこの先もあまりないと思うので勉強になった。

2016-10-02

Bloggerへのコメント欄サービスDisqusの導入

Bloggerの標準のコメント欄だと,コメントした人は返信があっても通知を受け取ることができず,その都度自分でコメントがついたかどうか確認する必要がある。これは閲覧者に不便を強いることになるのでよくないと思っていた。

それで,以前から気になっていたDisqusというコメントサービスを設置してみることにした。Disqusを使えば,コメントに対して返信があればメールで通知を受け取ることができる。また,各種SNSアカウントでもログインできるのでコメントが投稿しやすい。コメント欄サービスとしては,おそらく一番使い勝手がよさそうなサービスだ。

この記事ではBloggerへのDisqusのインストール手順を記す。

インストール手順の参考ページ

まず,以下のページに既に存在しているブログにDisqusをインストールするための手順が書かれているので,これを参考にする。

参考:Adding Disqus to your site | DISQUS

基本的にウィジェットが提供されているので,それを設置するだけでよさそうだ。

Disqusコメント欄の設置

以下の手順でDisqusのWebサイトへの登録ページヘ移動する。

Disqus | Install instructions for Blogger→[Blogger widget installation]を選択→右上の[Get Disqus for your site]

Disqusへのサイト登録ページへ移動

あるいは,以下の手順でトップページからアクセスしてもよい。

トップページ→[GET STARTED]→[I want to install Disqus on my site
トップページからのアクセス手順

Disqusで管理するWebサイトを登録するために,Webサイト名とジャンルや使用言語を設定する。なお,このWebサイト名はDisqusの管理ページのURLに使われる。

Disqusで管理するWebサイトの情報の登録

Webサイトの登録が終わると,管理ページに移動する。今回は,Website NameをMy Future Sight for Pastにしたので,以下のURLとなった。

https://my-future-sight-for-past.disqus.com/admin/install/

表示されたページから,設置するプラットフォーム(今回はBlogger)を選択する。すると,Disqusのインストール手順のページ(
https://my-future-sight-for-past.disqus.com/admin/install/platforms/blogger/)に移動する。

Disqusコメントの設置手順ページ

ここで,[1 Add my-fugure-sight-for-past to my Blogger site]を選択すると,新しいタブが開き,BloggerにDisqusのウィジェットを設置するかの確認画面が表示される。

Bloggerへのウィジェットの登録ページ

[ウィジェットを追加]を押下すると,ブログにDisqusが追加され,記事のコメント欄がDisqusになる。

設置されたDisqusコメント欄

既存コメントのインポート

Disqusのコメントを設置しただけでは,既存のBlogger側で管理しているコメントは見えなくなってしまう。既存のコメントをDisqus上で表示するには,BloggerのコメントをDisqusにインポートする。

先ほどの管理ページ(例:https://my-future-sight-for-past.disqus.com/admin/discussions/import/platform/blogger/)に戻り以下の項目を実行する。

[2 Import your existing Blogger comments into Disqus at Discussions > Import.]

[Discussions > Import]を押下すると,BloggerのコメントがDisqusにインポートされ,ついでにDisqusとBloggerとで記事のコメントが同期が始まる模様。

これで,ブログへのDisqusの最低限のインストールが完了した。

最新コメント一覧の表示

Disqusでは標準で最新コメント一覧のウィジェットは用意されていない。自分でAPIにアクセスするしかないようだ。

参考: Widgets | DISQUS

Bloggerで,ダッシュボード→[レイアウト]→[ガジェットを追加]→[HTML/JavaScript]を選択する。

レイアウト画面
ガジェットを追加
HTML/JavaScriptの設定
最新コメント一覧の追加

[HTML/JavaScriptの設定]に以下のコードを記入すれば,最新コメント一覧を表示できるウィジェットを配置できる。

<div id="recentcomments">
<script type="text/javascript" src="http://my-future-sight-for-past.disqus.com/recent_comments_widget.js?num_items=5&hide_avatars=0&avatar_size=12&excerpt_length=100"></script>
</div>

なお,my-future-sight-for-pastには,Disqusでサイトを登録するときに使用したIDを使う。その他のパラメーターは以下の役割となる。

Disqusの最新コメント一覧でのパラメーターの説明
パラメーター 規定値 説明
num_items 5 表示するコメント数
hide_mods 0 1にすれば管理者のコメントを非表示
excerpt_length 100 1コメントの表示文字数
hide_avatars 0 1にすればアバターアイコンを非表示
avatar_size 32 アバターアイコンのサイズ[px]

参考:How To Add Disqus Recent Comments Widget - Subin's Blog

これで,例えば以下の図のように最新コメントを表示させることができる。

最新コメント一覧の表示例

Disqusのコメント欄設置場所のカスタマイズ

既定のままだと,例えばモバイルページで表示されなかったり,プレビュー中でも表示されたりと都合が悪いのでカスタマイズする。以下のページを参考にした。

参照:BloggerへのDISQUS導入メモ | @ovreneli (tech)

BloggerのテンプレートのHTMLを直接編集する。ダッシュボードから以下の順番でテンプレート編集画面に移動する。

[テンプレート]→[HTMLの編集]

Bloggerのテンプレート編集画面
モバイルページでの有効化

まず,モバイルページでのDisqusのコメント表示を有効にする。Disqus forで検索して,b:widget要素を見つける。見つかったら,mobile='yes'を追加する。

モバイルページでのDisqusコメントの有効化
  <b:widget id='HTML4' locked='false' mobile='yes' title='Disqus for my-future-sight-for-past' type='HTML' visible='true'>

プレビュー中での無効化

続いて,Bloggerで記事を投稿画面のプレビュー表示ではDisqusを無効化する。上記HTMLコードのすぐ下あたりのコードを編集する。以下のコードを挿入して,プレビューページでは無効にする。

location.pathname !== &#39;/b/post-preview&#39; &amp;&amp;
プレビュー中のDisqusコメントの無効化
<b:if cond='data:blog.pageType == &quot;item&quot;'>
    <style type='text/css'>
        #comments {display:none;}
    </style>
    <script type='text/javascript'>
        location.pathname !== &#39;/b/post-preview&#39; &amp;&amp;
        (function() {
            var bloggerjs = document.createElement(&#39;script&#39;);
            bloggerjs.type = &#39;text/javascript&#39;;
            bloggerjs.async = true;
            bloggerjs.src = &#39;//&#39; + disqus_shortname + &#39;.disqus.com/blogger_item.js&#39;;
            (document.getElementsByTagName(&#39;head&#39;)[0] || document.getElementsByTagName(&#39;body&#39;)[0]).appendChild(bloggerjs);
        })();
    </script>
</b:if>
    <style type='text/css'>
        .post-comment-link { visibility: hidden; }
    </style>
    <script type='text/javascript'>
    location.pathname !== &#39;/b/post-preview&#39; &amp;&amp;
    (function() {
        var bloggerjs = document.createElement(&#39;script&#39;);
        bloggerjs.type = &#39;text/javascript&#39;;
        bloggerjs.async = true;
        bloggerjs.src = &#39;//&#39; + disqus_shortname + &#39;.disqus.com/blogger_index.js&#39;;
        (document.getElementsByTagName(&#39;head&#39;)[0] || document.getElementsByTagName(&#39;body&#39;)[0]).appendChild(bloggerjs);
    })();
    </script>
固定ページでの有効化

最後に,Bloggerの固定ページでもDisqusを表示させる。この方法は以下のDisqusの公式ページでも紹介されていたのでこの方法に従う。

参考:Add Disqus to Static Pages in Blogger | DISQUS

b:if要素の開始タグ<b:if cond='data:blog.pageType == &quot;item&quot;'>と終了タグ</b:if>だけを削除するかコメントアウトする。

固定ページでのDisqusコメントの有効化
  <b:widget id='HTML4' locked='false' mobile='yes' title='Disqus for my-future-sight-for-past' type='HTML' visible='true'>
<b:includable id='main'> <script type='text/javascript'> var disqus_shortname = &#39;my-future-sight-for-past&#39;; var disqus_blogger_current_url = &quot;<data:blog.canonicalUrl/>&quot;; if (!disqus_blogger_current_url.length) { disqus_blogger_current_url = &quot;<data:blog.url/>&quot;; } var disqus_blogger_homepage_url = &quot;<data:blog.homepageUrl/>&quot;; var disqus_blogger_canonical_homepage_url = &quot;<data:blog.canonicalHomepageUrl/>&quot;; </script> <!-- <b:if cond='data:blog.pageType == &quot;item&quot;'> --> <style type='text/css'> #comments {display:none;} </style> <script type='text/javascript'> location.pathname !== &#39;/b/post-preview&#39; &amp;&amp; (function() { var bloggerjs = document.createElement(&#39;script&#39;); bloggerjs.type = &#39;text/javascript&#39;; bloggerjs.async = true; bloggerjs.src = &#39;//&#39; + disqus_shortname + &#39;.disqus.com/blogger_item.js&#39;; (document.getElementsByTagName(&#39;head&#39;)[0] || document.getElementsByTagName(&#39;body&#39;)[0]).appendChild(bloggerjs); })(); </script> <!-- </b:if> --> <style type='text/css'> .post-comment-link { visibility: hidden; } </style> <script type='text/javascript'> location.pathname !== &#39;/b/post-preview&#39; &amp;&amp; (function() { var bloggerjs = document.createElement(&#39;script&#39;); bloggerjs.type = &#39;text/javascript&#39;; bloggerjs.async = true; bloggerjs.src = &#39;//&#39; + disqus_shortname + &#39;.disqus.com/blogger_index.js&#39;; (document.getElementsByTagName(&#39;head&#39;)[0] || document.getElementsByTagName(&#39;body&#39;)[0]).appendChild(bloggerjs); })(); </script> </b:includable>

まとめ

DisqusのBloggerへの導入手順を説明した。最新コメント一覧の設置や,固定ページでの有効化など一部HTMLやJavaScriptコードを編集する必要があってやや難しそうな印象をもった。しかし,一度設定してしまえばおしまいなので我慢しよう。

ブログにコメントがつくことはそんなになかった。しかし,コメントがついて返信してやりとりするときに,相手に通知が飛ばなくて手間をとらせてしまうのが恐縮だった。今後はこのようなことがなくなるので,自分の罪悪感もなくなり,お互い効率的に議論ができるようになる。

コメントの返信に対して訪問者へ通知が標準で送られるブログサービスはそうない。標準の機能は使いにくく,Disqusは世界中で使われており,品質が高いのでみんな導入したらよいと思う。