
2026/02/28 12:48
ANSI エスケープコードで自分だけのコマンドラインを構築する(2016)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
記事は、ANSI エスケープコードが端末の外観とカーソル位置を細かく制御できる方法を説明し、色付き出力、進捗バー、および対話型コマンドラインインターフェースなどの機能を可能にします。
主な機能
- 基本色(前景:
、背景:30–37
)と明るいバリエーション(40–47
);1
/\u001b[38;5;<ID>m
(ID 0–255)による 256 色サポート\u001b[48;5;<ID>m- カーソル移動:
(上/下/右/左)、次行・前行、列設定、位置設定などのコマンド\u001b[{n}A/B/C/D - 消去:画面
、行\u001b[{n}J
(\u001b[{n}K
)n=0,1,2
実用例
を使って前回の出力を上書きする単純な進捗バー\u001b[1000D- 空白行を印刷し、
で上に移動させることで複数の同時進行バーを実現\u001b[{count}A
とtty.setraw
を用いた最小限の Python REPL。表示ロジックは行頭へ移動し、消去して現在入力を印刷し、カーソル位置を再設定する。Enter、Backspace(127)、矢印キー(27 91 68/67)も処理sys.stdin.read(1)- ANSI コードを挿入することで構文ハイライトを追加可能。例:末尾空白は赤背景で強調
著者と背景
Haoyi は Scala ツールの Ammonite REPL と Mill Build Tool で知られ、コマンドラインユーティリティ開発経験を共有
結論
これらのエスケープシーケンスは、REPL、テキストエディタ、進捗バー、または任意の軽量対話型ツールなど、重いライブラリに頼らずに豊富な端末 UI を構築する基盤です。開発者が独自 CLI アプリケーションで実験・拡張できるよう奨励します。
本文
ターミナルに出力がスクロールして表示されることに慣れた皆さん、実際にはそれだけではありません。
プログラムはテキストの色を変えたり、カーソルを上下左右へ移動させたり、画面の一部を消去したりして、後で再描画するために使うこともできます。これが Git のダイナミックな進捗表示や Vim・Bash がスクロールせずに既存テキストを編集できる仕組みの根幹です。
Readline、JLine、Python Prompt Toolkit など、多言語で利用可能なライブラリもありますが、自分で実装することも十分可能です。この記事では、任意のコマンドラインプログラムからターミナルを制御する基本と、Python の例を通じてターミナルが提供するすべての特殊機能を直接利用する方法を紹介します。
著者について:Haoyi はソフトウェアエンジニアで、Ammonite REPL や Mill Build Tool など多くのオープンソース Scala ツールの作者です。この記事が気に入ったら、Haoyi の書籍 Hands‑on Scala Programming もぜひご覧ください。
ANSI エスケープコード
Unix ターミナルと対話するほとんどのプログラムは、ANSI エスケープコードを通じて行われます。これらはプログラムがターミナルに指示を送るために出力できる特殊なコードです。さまざまな端末でサポートされるコードのセットは異なるため、すべてのコードの意味を網羅した「権威ある」リストを見つけるのは難しいですが、Wikipedia などには妥当な一覧があります。
ANSI エスケープコードを利用するプログラムを書くことは可能であり、Ubuntu や OS‑X のような一般的な Unix システムでは動作します(Windows はここでは扱いません)。この記事では ANSI エスケープコードの種類と、それらを使ってインタラクティブなコマンドラインを最初から構築する方法を解説します。
リッチテキスト
-
色
- 8 色
- 16 色
- 256 色
-
背景色
-
装飾
カーソル移動
- 進捗表示
- ASCII プログレスバー
コマンドラインの構築
- ユーザー入力
- 基本的なコマンドライン
- カーソル移動
- 削除
- 完全性?
コマンドラインのカスタマイズ
結論
リッチテキスト
最も基本的な ANSI エスケープコードは、テキストを描画する際に使われるものです。これらは色や背景色、その他装飾を付けて出力しますが、特別な動作は行いません。出力された文字列はターミナルの下部に配置されスクロールしますが、デフォルトの黒/白ではなくカラーで表示されます。
色
テキストに色を付ける最も基本的な方法です。ANSI の色コードは次のようになります:
赤: \u001b[31m リセット: \u001b[0m
\u001b はほとんどの ANSI エスケープを開始する特殊文字です。Java、Python、JavaScript など多くの言語でこの表記が使えます。
例(Python 2):
print u"\u001b[31mHelloWorld"
Python 3 では先頭の
u を省略できます。出力後は必ず色をリセットしてください:
print u"\u001b[31mHelloWorld\u001b[0m"
8 色
基本端末には 8 色があります。
| 色 | コード |
|---|---|
| 黒 | \u001b[30m |
| 赤 | \u001b[31m |
| 緑 | \u001b[32m |
| 黄 | \u001b[33m |
| 青 | \u001b[34m |
| マゼンタ | \u001b[35m |
| シアン | \u001b[36m |
| 白 | \u001b[37m |
| リセット | \u001b[0m |
各色の文字を 1 文字ずつ表示する例:
print u"\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m" print u"\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m"
16 色
太字(明るい)色は
;1 を付けて表現します。
| 色 | コード |
|---|---|
| 明るい黒 | \u001b[30;1m |
| 明るい赤 | \u001b[31;1m |
| 明るい緑 | \u001b[32;1m |
| 明るい黄 | \u001b[33;1m |
| 明るい青 | \u001b[34;1m |
| 明るいマゼンタ | \u001b[35;1m |
| 明るいシアン | \u001b[36;1m |
| 明るい白 | \u001b[37;1m |
| リセット | \u001b[0m |
256 色
16 色を超えると、いくつかの端末は 256 色セットをサポートします:
\u001b[38;5;<ID>m
例(Python):
import sys for i in range(0, 16): for j in range(0, 16): code = str(i * 16 + j) sys.stdout.write(u"\u001b[38;5;" + code + "m " + code.ljust(4)) print u"\u001b[0m"
背景色
背景色は前景色のコードを反映したものです。
| 色 | コード |
|---|---|
| 黒 | \u001b[40m |
| 赤 | \u001b[41m |
| 緑 | \u001b[42m |
| 黄 | \u001b[43m |
| 青 | \u001b[44m |
| マゼンタ | \u001b[45m |
| シアン | \u001b[46m |
| 白 | \u001b[47m |
明るいバージョンは
;1 を付け、前景色の明度に影響します。
print u"\u001b[40m A \u001b[41m B \u001b[42m C \u001b[43m D \u001b[0m"
256 色背景は
48;5;<ID> を使用します。
装飾
その他の装飾コード:
| 装飾 | コード |
|---|---|
| 太字 | \u001b[1m |
| 下線 | \u001b[4m |
| 逆転 | \u001b[7m |
例:
print u"\u001b[1m BOLD \u001b[0m\u001b[4m Underline \u001b[0m\u001b[7m Reversed \u001b[0m"
カーソル移動
ANSI エスケープコードはカーソルを自由に移動させたり、画面の一部を消去したりできます。基本的な移動コマンド:
| 移動 | コード |
|---|---|
| 上へ | \u001b[{n}A |
| 下へ | \u001b[{n}B |
| 右へ | \u001b[{n}C |
| 左へ | \u001b[{n}D |
進捗表示
単純なパーセンテージ表示例:
import time, sys def loading(): print "Loading..." for i in range(0, 100): time.sleep(0.1) sys.stdout.write(u"\u001b[1000D" + str(i+1) + "%") sys.stdout.flush() print loading()
\u001b[1000D はカーソルを画面左端に移動させ、古いパーセンテージを上書きします。
ASCII プログレスバー
import time, sys def loading(): print "Loading..." for i in range(0, 100): time.sleep(0.1) width = (i+1) // 4 bar = "[" + "#" * width + " " * (25 - width) + "]" sys.stdout.write(u"\u001b[1000D" + bar) sys.stdout.flush() print loading()
コマンドラインの構築
コマンドラインは単に ANSI コードでターミナルと対話するプログラムです。以下は Python で最小限実装した例です。
import sys, tty def syntax_highlight(inp): stripped = inp.rstrip() return stripped + u"\u001b[41m" + " " * (len(inp) - len(stripped)) + u"\u001b[0m" def command_line(): tty.setraw(sys.stdin) while True: # 各行ごとにループ input_str = "" index = 0 while True: # 各文字ごとにループ char = ord(sys.stdin.read(1)) if char == 3: # CTRL‑C return elif 32 <= char <= 126: input_str = input_str[:index] + chr(char) + input_str[index:] index += 1 elif char in {10,13}: # Enter sys.stdout.write(u"\u001b[1000D") print "\nechoing... ", input_str input_str = "" index = 0 elif char == 27: # エスケープシーケンス(矢印キー) next1, next2 = ord(sys.stdin.read(1)), ord(sys.stdin.read(1)) if next1 == 91: if next2 == 68: # 左 index = max(0, index-1) elif next2 == 67: # 右 index = min(len(input_str), index+1) elif char == 127: # Backspace if index > 0: input_str = input_str[:index-1] + input_str[index:] index -= 1 # 現在の行を再描画 sys.stdout.write(u"\u001b[1000D") # 行頭へ移動 sys.stdout.write(u"\u001b[0K") # カーソル以降を消去 sys.stdout.write(syntax_highlight(input_str)) sys.stdout.write(u"\u001b[1000D") # 再度行頭へ if index > 0: sys.stdout.write(u"\u001b[" + str(index) + "C") sys.stdout.flush() command_line()
上記コードのポイント
を保持し、現在カーソル位置を追跡。index- 可視文字・Enter・矢印キー(左/右)・Backspace の入力を処理。
でカーソル以降を消去してから再描画。\u001b[0K- シンプルな構文ハイライト関数で末尾空白を背景色付きで表示。
コマンドラインのカスタマイズ
syntax_highlight を好きなものに差し替えれば、Pygments など本格的な言語ハイライトも実装可能です。キーシーケンスを解釈して適切な ANSI コードを出力すれば、ドロップダウンメニューや Shift+矢印での選択・ブロックインデントなども追加できます。
結論
ANSI エスケープコードだけで最小限のコマンドラインを実装できることは、基本制御命令を理解すれば驚くほど簡単です。そこから構文ハイライト、多行編集、カスタムプロンプト、さらにはターミナルゲームまで、入力シーケンスとカーソル操作・テキスト操作のマッピングで実装できます。
リッチなターミナルインタラクション(進捗バー、編集可能なプロンプト、カスタムエディタ)が必要になったら、これらの ANSI コードを知っておくことで、自分だけの機能を構築する土台が手に入ります。