アイキャッチ

プロフィール写真

大阪大学 大学院理学研究科 助教

村瀬 功一

2015年に東京大学大学院理学系研究科にて博士(理学)を取得。国内外の研究機関で特任研究員などを歴任し、2026年より現職。専門は極限状態の物質(クォーク・グルーオンプラズマ、中性子星など)の理論物理。2013年にコマンドラインエディタ「ble.sh」の開発を開始。現在も本業の研究の傍ら、同ソフトウェアのメンテナンスを続けている。

GitHub: akinomyoga ble.sh

Zsh、Fish (※1)。高機能でモダンなシェルも存在する中、なぜ、その研究者はほぼ素の Bash のみを用いて、コマンドラインエディタに「色」を付けようとしたのか――。

GitHub で4,500超のスターを獲得し、週間のクローン数約3,000回を維持している「ble.sh(Bash Line Editor)」という国産のオープンソースソフトウェアがあります。

標準の Bash の入力環境には、コマンドの色分け(シンタックスハイライト)や、入力履歴からのオートサジェストといった機能は備わっていません。

そんな中、コマンドラインエディタ(行エディタ)である ble.sh は、これらの機能も含めて、エディタのコア部分が純粋な Bash スクリプトで書かれています。外部プログラムへの依存を最小限に抑え、ターミナルの入力・描画環境をゼロから再構築するという、特異なアプローチをとっているのです。そしてシンタックスハイライトやオートサジェストにとどまらず、Vim の編集モード(※2)を再現する機能まで実装されています。

開発者は、専業のソフトウェアエンジニアではありません。宇宙初期の極限状態や中性子星の内部に関係する、理論物理の研究者である村瀬功一さんです。

なぜ、「Bash のみ」という制約の中でソフトウェアをつくり込もうと思ったのか? そして、標準機能だけでエディタを自作しようとすると、どのような技術的壁に直面し、どのような工夫を強いられるのか。

「最初は数百行くらいでいけると思ったのですが……」と語る村瀬さんに、10年以上にわたる開発の軌跡についてお聞きしました。

ble.shの公式デモ(v0.2)
▲ble.shの公式デモ(v0.2)
(※1)Zsh、Fish:UNIX 系 OS で利用できる代替シェル。Fish は標準で強力な補完や色付け機能を備えるほか、Zsh は拡張性が高く現在の macOS の標準対話シェルとしても知られる。
(※2)Vim モード:テキストエディタ「Vim」特有の、ノーマルモードや挿入モードといった操作体系(キーバインド)を ble.sh 上で再現する機能。

「面白そう」で始めてエディタから自作

――まず、2013年に「ble.sh」の開発をスタートした経緯を教えてください。

村瀬:ある技術ブログの記事を読んだことが直接のきっかけです。

Zsh 5.0.0 に追加されたコマンドを色付けするための API と、それを利用した外部プラグイン(zsh-syntax-highlighting)を紹介する内容でした。それを見たとき、私は直感的に「これ、Bash の標準機能でもできるんじゃないか?」と思ったのです。

もちろん、Bash の標準入力機能である GNU Readline が提供する API は、そもそもグラフィカルな表示を想定してつくられていません。

それでも、Bash の機能を組み合わせて「トリック」じみたことをすれば、無理やり色を付けられるかもしれない。実現できたら面白そう……そう考えて、手元で実験を始めたのが最初でした。

――ご自身の研究環境でも、Bash を使っていたのでしょうか。

村瀬:はい。

当時は大学院に在籍していましたが、その時から、研究の数値計算で計算機クラスタを動かすために Linux サーバーにログインしてターミナル上で作業をしていました。その過程で日常的に Bash には触れていました。

――ということは、実用的な意味でも「ターミナルに色を付けたい」という思いがあったのでしょうか。

村瀬:次第にそういう欲求も芽生えていきましたね。

一般的なプログラミング用のエディタでは、構文ごとに色が付くのが当たり前です。私も昔から趣味でプログラミングをしていて、そうした色付きのコードに慣れていたので、白黒しかない Bash の画面に対して、「なんだかな」という物足りなさは感じていました。

それに、実際に Zsh や Fish といったモダンなシェルの標準機能やプラグインを触ってみると、入力した文字列が実在するコマンドかどうかで色が変わったり、存在するファイルパスを入力すると下線が引かれたりします。自分でも試してみて、視覚的にエラーや打ち間違いがすぐに分かるのは、非常に便利だと感じていました。

なので、次第に実用的な意味でも「自分が普段使っている Bash にもこの機能が欲しい」と思うようになりました。もちろん、根っこにあるのは「Bash の機能を組み合わせて実現できたら楽しそう」という知的好奇心でしたが。

村瀬さん
▲村瀬さんは中学生の頃から趣味でプログラミングに取り組み、最初は Visual Basic 、その後は C# などをよく書いていたという。天体シミュレーションや、入力された文字列を文法に沿って解釈する構文解析器(パーサー)などを自作して遊んでいたとのこと。
村瀬:とはいえ、最初は「数百行くらいスクリプトを書けば、簡単にできるだろう」と高を括っていました。Bash が文字を出力する部分にうまく割り込んで、色を乗せれば済むと思ったんです。

――実際に数百行程度で、思い通りにいきましたか。

村瀬:全然、そんなことはありませんでした。

実際につくり始めてみると、ちょっと色を足すだけではうまくいかないことがすぐに分かりました。というのも、私はあくまで外部ツールに頼らず、Bash 標準のスクリプト機能だけで完結させるという縛りで開発しようとしていたからです。

――というと?

村瀬:Bash には bind -x という、特定のキー入力に対して好きなシェルコードを実行できる機能があります。これを使って、Bash の標準入力機能である GNU Readline の動作に介入し、カラフルな文字を描画して上書きしてしまおうと考えました。

ところが、ここで致命的な問題にぶつかりました。当時、シェルコードでは、Readline が本来持っている「カーソルを動かす」「文字を消す」といった編集コマンドを直接呼び出すことができなかったのです。

――Bash の標準機能であるはずなのに、Bash のスクリプトからは操作できなかったのですか?

村瀬:はい。Readline は Bash に標準搭載されてはいますが、中身はC言語で書かれた独立したライブラリなんです。スクリプト側から「いつもの動作をして」と直接命令する仕組みがありません。

さらに、Bash が画面を描画するタイミングの仕様も壁になりました。キーが押されて私のスクリプトが呼び出され、画面にカラフルな文字を出力したとします。ところがスクリプトの処理が終わった直後、今度は Bash 本体がもともと持っているモノクロのテキストを使って、最新の状態で画面全体を上書きしてしまうのです。

――せっかく色を付けても、一瞬で元に戻されてしまうと。

村瀬:ええ。つまり、Bash スクリプトだけで入力した文字を解釈し、色を計算し、ターミナル上に描画するためには、既存の Readline の機能を完全に無効化しなければなりませんでした。カーソルの移動から何から、自分で一からエディタそのものをつくり直す必要があったのです。

当初、8,000行ほどのスクリプトを書いて色を付けること自体には成功したのですが、とにかく動作が遅い。おまけに、先ほどの Bash による上書きを無理やり防ぐために、文字を打つたびに画面を全消去して再描画するような処理になったため、激しいちらつきが発生し、あまり実用的ではありませんでした。

――その遅さの問題を解決する方策は、当時の時点ですでに見えていたのでしょうか。

村瀬:色々と高速化のアイディアはありました。ただ、私としては「原理的に Bash だけでエディタをつくれる」ということが結論づけられた時点で、ある種の満足感を得てしまいました。

困難なパズルをどうやって解くか? その道筋を見つけるところまでが、一番面白いんですね。

解決策が見えて、あとは地道に数万行の実装を重ねるだけだと分かった瞬間に、「まあ、後はいいか」と思ったのです。それで、そのまま2年ほど放置してしまいました。

ただ、2015年には博士論文の執筆にめどが立ち、まとまった時間がとれました。その時に狂ったように実装を進め、速度関連の問題も解決することができました。

そうして2015年の終わりごろに ble.sh として公開した、という経緯です。

どこでも動かすために「Bash」縛り:ピュア Bash でインクリメンタルパーサー

――今更なのですが、なぜそこまでして、エディタ部分を基本的に「Bash スクリプトのみで完結させる」ことを貫いたのでしょうか。

村瀬:面白いからというのもありますが、現実的な理由としては「どんな環境でも動く」ようにもしたかったからです。

個人のパソコンであれば、Zsh や Fish など、好きなシェルを自由にインストールできます。しかし、研究で使うスーパーコンピューターや、古くから稼働しているサーバーなどの環境では、自分に管理者権限がないことや環境の特殊さのため、新しいツールを勝手に入れることが面倒なのです。

それに、シェルというのは、手に染みついた無意識の「癖」でキー操作を行うものです。なのに、ログインする環境ごとに設定や操作感が変わってしまうのは、大きな苦痛を伴います。

だからこそ「Bash さえあれば、どこでも同じように、シンタックスハイライト付きの快適な操作体験が得られる」という状態をつくりたかったのです。

村瀬さん

――スクリプトのみで実装を進める上で、工夫した点はなんでしょうか。

村瀬:ひとつはやはり「速度をどうするか」でしょうか。

通常、Bash でスクリプトを書く際は、テキストの検索や置換を行うために grep や awk、sed といった外部のコマンドを呼び出して使うのが当たり前です。これらは大抵の環境に標準で備わっているので、「どこでも動かせるようにする」という目的からは外れません。

しかし、エディタとして実用的な速度を出すためには、コアの処理において標準ツールにほとんど頼れませんでした。多くのアルゴリズムを、Bash の内蔵機能だけで自作しなければならなかったのです。いよいよ外部コマンドすら用いない「ピュアな Bash」で書く必要に迫られたのは大きな挑戦でしたね。

――そうした外部コマンドすら使ってはいけなかったのは、なぜなのでしょうか。

村瀬:外部のプログラムを呼び出すたびに、OS 側では新しいメモリ空間を割り当ててプロセスを生成する、 fork/exec の処理が発生するからです。

これにはシステムによって、数ミリ秒から数十ミリ秒の時間がかかります。

一般的な計算処理であれば、その程度の遅延はあまり気にならないでしょう。

しかしこれがエディタとなると、ユーザーがキーボードを叩くたびに、文字の解釈や色の計算を行うわけです。1文字打つたびにこのプロセス生成を行っていては、わずかな遅延が何度も発生します。普段使いのラインエディタとして用いるには、非常に大きなストレスです。

――とはいえ、Bash の内蔵機能だけでエディタの複雑な処理をすべて自作するというのは、言語の仕様的にかなり無理があるのではないでしょうか。

村瀬:おっしゃる通り、Bash はあくまでインタープリタであり、C++ などに比べれば実行速度は非常に遅いです。

おまけに変数は「文字列」しか扱えず、数値の計算も内部的にはいちど文字列へと変換して行われます。配列もフラットなものしかなく、多次元配列のような、データの入れ子構造すらつくれません。

そうした制約の中で複雑な処理を実現するため、データ構造の面ではかなり変則的な工夫を強いられました。

また、文法どおりに色をつけるには、入力された行の構造を解釈する構文解析が必要です。これも外部ツールには頼れないので、パーサーも Bash で自作しました。ただ、普通のパーサーのままでは、エディタとしては動作が重くなりすぎるのですね。そこで、「インクリメンタルパーサー」という仕組みを実装しています。

インクリメンタルパーサーの実装に関するコメント部分
▲村瀬さんが特に力を入れたと話す、Bash にて実装されたインクリメンタルパーサーのコメント部分。GitHub 上にて core-syntax.sh 内よりスクリーンショット。同ファイルだけで約8,000行ある。Bash は配列を入れ子にできないため、構文木の親子・兄弟関係を、要素同士の距離を示す数値だけで表している。

  for ((i=i1;i<iN;)); do
    ble/syntax/parse/serialize-stat
    if ((i>=i2)) && [[ ${tail_syntax_stat[i-i2]} == "$sstat" ]]; then
      if ble/syntax/parse/nest-equals "$inest"; then
        # 前回の解析と同じ状態になった時 → 残りは前回の結果と同じ
        _ble_syntax_stat=("${_ble_syntax_stat[@]::i}" "${tail_syntax_stat[@]:i-i2}")
        _ble_syntax_tree=("${_ble_syntax_tree[@]::i}" "${tail_syntax_tree[@]:i-i2}")
        _ble_syntax_nest=("${_ble_syntax_nest[@]::i}" "${tail_syntax_nest[@]:i-i2}")
        _ble_syntax_attr=("${_ble_syntax_attr[@]::i}" "${tail_syntax_attr[@]:i-i2}")
        break
      fi
    fi
    _ble_syntax_stat[i]=$sstat

▲core-syntax.sh 内より、インクリメンタルパーサーの実装の一部。通常のパーサーは文字を打つたびにコード全体を解析し直すが、Bash では重くなる。そこで、前回の解析から変更のあったコードだけを再解析する。前回の解析における解析状態を読み込んで、解析を途中から再開する。新しい解析状態が前回と一致した時点で、残りは前回の結果を配列ごと継ぎ足して解析を打ち切る。また、Bash には構造体や多次元配列がないため、状態(stat)や構文木(tree)などを別々のフラットな配列として用意し、同じインデックスで並行管理している。

――なんと申しますか、「重い処理を避けるために『ピュア Bash』 を選んだが、Bash 自体がそもそも重い」という……大変な挑戦をすることになっていませんか?

村瀬:そこは確かに、傍から見ると面白く感じるかもしれません。ですが、それが現実的な策だったのです。

実際に外部プログラムを呼び出す際のプロセスの生成コストと、Bash の中で無理やり計算させる内部処理コストを比較してみると、エディタのような文字単位の細かい処理においては、すべて Bash の内部で計算しきってしまった方がトータルで速かったんですね。なので、この手法をとりました。

――他に、速度を確保するために取り入れた手法はありますか。

村瀬:入力のレスポンスを妨げないための、非同期処理の実装です。

例えばオートサジェストの機能では、ユーザーがキーを叩いている間に、ble.sh が裏側でコマンド履歴を検索したり、ファイルパスを確認したりする重い処理を走らせる必要があります。しかし、Bash には標準で裏側に別のスレッドを立ち上げるような非同期処理の機能がありません。

そこで、重い処理を細かく分割し、ユーザーのキー入力が途切れた数ミリ秒の隙間を見計らって、都度、バックグラウンドで少しずつ処理を進めるという、疑似的なイベントループの仕組みをつくりました。ユーザーが新しいキーを叩いて入力をキャンセルした場合は、それ以降のバックグラウンド処理を即座に中断することもできます。

適度に注目されるくらいがいい

――ble.sh への反響について教えてください。

村瀬:公開した直後の反響は「静寂」でしたね。ニッチなツールですし、国内ではあまり多くの人の目にはとまりませんでした。

――ですが、現在では GitHub で4,500以上のスターを獲得しています。どのようにして広まっていったのでしょうか。

村瀬:転機は大きく2つあります。基本的には、他の方が別のコミュニティで ble.sh を取り上げてくれたことがきっかけです。

まずは「Oil Shell」(現 Oils)という、既存の Bash との互換性を保ちつつ独自の拡張を試みているシェルの開発者が、ble.sh をブログで紹介してくれたこと。この開発者は、自身のプロジェクトで Bash との互換性をテストするため、複雑なBash スクリプトを探していたようです。そんな中、ble.sh の存在を知り、実際に試してみて驚いていただけた様子でした。

――Bash との互換性を保ちつつ拡張を目指すプロジェクトを進めていたところ、たまたまBash の限界に迫るような構造をしている ble.sh に出会って驚いたのですね。

村瀬:もう1つのきっかけは、ターミナルのコマンド履歴を複数のPC間で同期する「Atuin」という人気のツールです。

当時、 Atuin を標準の Bash と組み合わせて使うと、 Bash の仕様上の制約からいくつかの不具合が発生していたため、 Atuin 側のリポジトリに Issue が続出していました。Atuin はコマンド実行前と実行後に起動し、コマンドとその実行時間を記録します。また、Atuin は専用の検索 UI を表示し、選択したコマンドを入力行に挿入して自動で実行します。しかし、Bash の標準入力機能である Readline では、外部から処理を差し込んだりコマンドを自動実行したりする仕組みが提供されません。無理に実現しようとするとハックに頼らざるをえず、コマンドの記録間違いや表示の乱れが発生します。

一方 ble.sh は入力行の管理からコマンドの実行までをすべて自前で管理しており、外部ツールが処理を差し込んだりコマンドを実行したりできる API も備えています。つまり、標準入力機能の Readline ではなく、ble.sh の上であれば、無理なく Atuin の処理を差し込むことができ問題も起きない。

そこで、ある時 ble.sh の存在を知った Atuin 開発者が公式ドキュメントに「Atuin は、Bash では ble.sh と組み合わせて使うと最良に動作する」と明記してくれました。Atuin は数万スターを集める人気のプロジェクトなので、引っ張られる形で ble.sh の認知度も一気に伸びました。

こうした経験を経て、注目を集めるには他の方に広めていただくことが大事なのだなと実感しました。自分で「こんなものをつくりました」と宣伝しても、基本的には興味を抱かれづらいようです。

――個人的な知的好奇心からスタートしたプロジェクトが、世界から一定の注目を集めた。ご自身では、そのことをどのように受け止めていますか。

村瀬:まず、自分でつくったツールについては、「ほどほどに注目されればいい」と思っています。その意味で、今ぐらいがちょうどいいバランスかもしれません。

正直、「制約の中で目的のために何をするか」という技術的な工夫を評価軸にするならば、我ながら、それなりに関心を集めてもいいプロジェクトなのではないか? という自負はありました。とはいえあまりにも Issue が殺到してしまうと、キャパシティ的に対応が難しくなるので、現状の規模感が性に合っています。

――それにしても研究生活と並行して10年以上、 ble.sh のメンテナンスを続けています。そのモチベーションはどこにあるのでしょうか。

村瀬:正直に言うと、最近は少し面倒だと思うこともあります(笑)。この10年間も、ずっと全力で開発してきたわけではなく、特定の時期に集中してリソースを割いていたに過ぎません。

今はあくまで自分が日常的に使うためのツールとして、最低限の修正やメンテナンスを行っている、というスタンスです。

途中で実装した Vim モードにしても「どうしても自分で使いたかったから」というよりは、リクエストが寄せられ、かつ有志が熱心にテストに協力してくれたから実装した、という側面が強いです。

――では今後、 ble.sh に新しく追加したい機能の構想などはありますか。

村瀬:「時間ができたらやりたいことリスト」には、いくつかの項目があります。 例えば、文法のエラーにただ色を付けるだけでなく「このコマンドのこのオプションは間違っていますよ」と具体的な使い方までチェックしてエラーメッセージを出す機能。あるいは、最近流行しているAIエージェントを組み込んで、自然言語から次のコマンドをサジェストさせる仕組みなども、実現できれば面白いだろうなとは思います。

ただ、どうしても本業で忙しいので、あくまで時間ができたときに自分のペースで気が向いたら進めていけたら、という程度です。

何せ、私としてはいちどやり切った感覚が強いのです。最初こそ勢いでつくりましたが、「インクリメンタルパーサー」の仕組みのように、内部の機構をどうやって設計するかを自分なりに考え抜いて、実際に動く形につくり上げられた。その時点で、知的好奇心としては十分満足しています。

村瀬さん
取材・執筆:田村 今人
撮影:曽川 拓哉