2016年5月14日

mkshにてviモードを使用した際の問題点まとめ(Ver.R52時点)

mksh(MirBSD Korn Shell)は、MirOS BSDプロジェクトで開発されている、kshのフリーソフトウェア実装である。
軽量でありながら、ヒストリ、ブレース展開、パイプラインにおける各プロセスの終了ステータスの取得など、実用性の高い機能も兼ね備えている。
そのため、dashとは異なりインタプリタとしてだけでなく、対話シェルとしても有用なシェルである。

しかし、私がmkshを対話シェルでviモードとして利用したところ、一般的にはあまり知られていないと思われる問題点を、幾つか発見した。
そこで、同じ環境で利用する人への参考として、また個人的なメモとして、問題点を記事にまとめた。


環境

  • Arch Linux (x86_64, 4.5.3-1-ARCH)

  • mksh (MIRBSD KSH R52 2016/03/04)

  • USP Personal Tukubai (20160402, ketaコマンドのみを実行例の整形に使用)

問題点

mkshをviモードで使用した際の問題点は、次の2つである。

  1. プロンプトがUTF8に完全対応していない

  2. キーバインドを変更できない。

以下、それぞれ詳細に解説する。

プロンプトがUTF8に完全対応していない

mkshには、UTF8に対応するためのオプション"utf8-mode"が用意されており、公式ではマルチバイト文字に対応している。
しかし、この"utf8-mode"は、emacsもしくはgmacsモードのみしか対応していない。
そのため、viモードで利用した場合、mkshはプロンプト上でマルチバイト文字を適切に扱うことが出来ない。

以下、例を挙げる。

(emacsモードでの例)

$ set -o emacs -o utf8-mode  # emacsモードおよびutf8対応モードの有効
$ set -o | grep -E '(emacs|vi|utf8-mode) ' | keta  # 設定の確認
  allexport off inherit-xtrace  on noexec off   pipefail off      utf8-mode  on
braceexpand  on        keyword off  nohup  on privileged off             vi off
      emacs  on          login  on  nolog off restricted off vi-esccomplete off
$ echo ほげほげ^H^H^H^H | tee test1  # "ほげほげ"に対して、<Backspace>を4回入力して実行

$  # 期待したとおり、改行のみが出力される
$ od -t cx1 test1  # 出力の詳細な内容
0000000  \n
         0a
0000001

(viモードでの例)

$ set -o vi -o utf8-mode  # viモードおよびutf8対応モードの有効
$ set -o | grep -E '(emacs|vi|utf8-mode) ' | keta  # 設定の確認
  allexport off inherit-xtrace  on noexec off   pipefail off      utf8-mode  on
braceexpand  on        keyword off  nohup  on privileged off             vi  on
      emacs off          login  on  nolog off restricted off vi-esccomplete off
$ echo ほげほげ^H^H^H^H | tee test2  # "ほげほげ"に対して、<Backspace>を4回入力して実行
ほげ
    $  # 4バイト分しか削除されず、プロンプトも含めた出力が妙な事になってしまう...
$ od -t cx1 test2  # 出力の詳細な内容
0000000 343 201 273 343 201 222 343 201  \n
         e3  81  bb  e3  81  92  e3  81  0a
0000011

このように、mkshのviモードでは、プロンプト上でマルチバイト文字を適切に扱うことが出来ない。

キーバインドを変更できない

他のシェルと同様に、mkshには組込コマンドとしてbindが実装されており、キーバインドを自由に変更することが可能になっている。
しかし、このbindによるキーマップの定義は、emacsモードにのみ適用され、viモードでのキーマップの定義は不可能である。

この理由は、実装にある。
ソースコードを読むと、viモードのデフォルトのキーバインドは、ハードコーディングされている。
具体的には、edit.cのvi_insert()やvi_cmd()等に、"case CTRL('r'):"といった様に実装されている。
そのため、viモードでは、bindコマンドによってキーマップを再定義することが、不可能になってしまっている。

対策

上記2点の問題に対する対策は、残念ながら今のところは無い。

参考にしたもの

  1. $ man mksh

  2. "MirOS: mksh – the MirBSD Korn Shell" https://www.mirbsd.org/mksh.htm

  3. "MirBSD/mksh: MirBSD Korn Shell Source Code Mirror - GitHub" https://github.com/MirBSD/mksh

考察

※以下、執筆者の推測になります。

今回、mkshのviモードにおける問題点をまとめた際に感じたこと。
mkshのviモードって、実はとりあえず実装しただけの機能じゃないのかな?
そう感じた理由は、次のとおり。

  1. 現在でも開発が続いているにも関わらず、未だにUTF8に完全対応していない

  2. viモードのキーマップは、コード内にハードコーディングされている

  3. シェルにおけるviモードは、POSIX(IEEE Std 1003.1, 2013 Edition)で定められている

  4. シェルにおけるviモードは、emacsモードと比較してマイナーである(明確な根拠は無い…​)

以下、補足説明。

1.現在でも開発が続いているにも関わらず、未だにUTF8に完全対応していない
本文で説明したとおり。このご時世なのにUTF8に完全対応していないのは、ちょっと不自然な気がする。
emacsモードは完全対応しているのに、viモードは対応していない点も、更に変な感じ。

2.viモードのキーマップは、コード内にハードコーディングされている
言わずもがな。コードの再利用性や、ソフトウェアの拡張性を考慮すれば、ハードコーディングはしないはず。
emacsモードではキーマップがきちんと定義されているので、何だか統一感が無い。
更にemacsモードは、"emacsfn.h"でキーマップの再定義用のコードが書かれているのに、viモードでは該当するコードが存在しない点でも、何だか疎かにされているような気が。

3.シェルにおけるviモードは、POSIX(IEEE Std 1003.1, 2013 Edition)で定められている
これは、Twitterにて @bsdhackさんに教えていただいた情報。

mkshは、POSIX互換を謳っている。ということは、POSIX互換を謳うためには、POSIXで定められているviモードを実装する必要がある。
このことが、次の理由に関係してくると思った。

4.シェルにおけるviモードは、emacsモードと比較してマイナーである(明確な根拠は無い…​)
これはあくまで僕の主観。シェルにおけるviモードは、emacsモードと比較して、使用人口が少ないと思う。
根拠としては弱いけれど、どのシェルでもデフォルトのキーバインドは、emacsモードになっているので。
(これまた根拠としては弱いけれど…​ Googleで検索した結果も、emacsモードの方がヒット数が多いので。)

そして、利用者が少ないということは、実装する際に後回しにされやすいのでは…​と推測した。


雑記

  • 🐺。oO(誰も実装しないなら、僕がやるしかないのか…​??)
    → …​でも、シェルを改良するだけのC言語コーディング力が無いorz

  • 本文中にも書いたのだけれど、mkshって、軽量なのにブレース展開や$PIPESTATUSも使えるので、対話シェルとしてはバランスが取れていて優秀だと思う。
    → なのに僕の大好きなviモードには、上記のような問題点があるので、実際にはすごく使いづらい…​ とっても勿体無い…​…​

  • 単に「軽量」と書いても分からないので、簡単なベンチマークで比較してみた。
    → 比較するシェルは、zsh、bash、mksh、dashの4つ。 → 測定方法は、dateをwhile構文で延々と実行し、その出力をuniq -cへ渡して、一秒あたりの実行回数を計測するだけ。

$ zsh -l
$ while :; do date; done | uniq -c
     78 2016年  5月 14日 土曜日 00:04:17 JST
    209 2016年  5月 14日 土曜日 00:04:18 JST
    210 2016年  5月 14日 土曜日 00:04:19 JST
    211 2016年  5月 14日 土曜日 00:04:20 JST
    208 2016年  5月 14日 土曜日 00:04:21 JST
    213 2016年  5月 14日 土曜日 00:04:22 JST
^C

$ bash -l
$ while :; do date; done | uniq -c
    250 2016年  5月 14日 土曜日 00:05:59 JST
    617 2016年  5月 14日 土曜日 00:06:00 JST
    600 2016年  5月 14日 土曜日 00:06:01 JST
    518 2016年  5月 14日 土曜日 00:06:02 JST
    452 2016年  5月 14日 土曜日 00:06:03 JST
    482 2016年  5月 14日 土曜日 00:06:04 JST
^C

$ mksh -l
$ while :; do date; done | uniq -c
    446 2016年  5月 14日 土曜日 00:07:23 JST
    747 2016年  5月 14日 土曜日 00:07:24 JST
    744 2016年  5月 14日 土曜日 00:07:25 JST
    731 2016年  5月 14日 土曜日 00:07:26 JST
    740 2016年  5月 14日 土曜日 00:07:27 JST
    740 2016年  5月 14日 土曜日 00:07:28 JST
^C

$ dash -l
$ while :; do date; done | uniq -c
    161 2016年  5月 14日 土曜日 00:07:49 JST
    821 2016年  5月 14日 土曜日 00:07:50 JST
    843 2016年  5月 14日 土曜日 00:07:51 JST
    844 2016年  5月 14日 土曜日 00:07:52 JST
    842 2016年  5月 14日 土曜日 00:07:53 JST
    831 2016年  5月 14日 土曜日 00:07:54 JST
^C
  • こうやって比較すると、mkshはそこそこ高機能なのにdashに匹敵するほど速くて、実はかなり優良なシェルだと思う。
    → と思うからこそ、viモードに問題点があるのは、ますます勿体無い…​…​

Tags: Shell *nix 個人メモ
このエントリーをはてなブックマークに追加