色付きのハイライトにて実行結果を表示するコマンドが多くありますが、その結果を色付きのままHTMLで保存する方法を説明します。
LinuxやWSLなどでコマンドを実行したとき、出力がカラーで表示されることがあると思います。
たとえばls --color=autoとかですね。このような出力を普通にリダイレクションなどでファイルに書き込むと色情報が失われて普通の文字になってしまいます。
lsだったら別にそれで困らないと思いますが、色がないとわかりづらい diff のようなコマンドの出力だと色情報ごと保存したいときもあると思います。
そのような場合において今回はコマンド出力をHTMLに変換して色ごと保存する方法を説明します。
色付き文字を出力する方法
まず最初に基礎知識として文字に色を付ける方法を簡単に説明します。 ANSIエスケープシーケンスを使います。そんなの知っているよ!というかたは飛ばしてOKです。
ASCII コード
通常文字(アルファベットと記号と数字。いったん日本語文字は置いておいてください)を出力をするにはASCIIコードを用います。 CPUは結局数値しか扱えないので文字を表現しようとすると数値を文字に対応させる必要があります。
その対応表がASCIIコード表と呼ばれるものです。たとえば「A」は65、「B」は66、「C」は67…. となっています。 実際のASCIIコード表はググれば出てきます。Wikipedia - ASCIIなどが参考になるかと思います。 いろいろむずかしいこと書かれていますが、要は「ASCII印字可能文字」のところの表に合わせて数字を文字として解釈するよ、というだけの話です。
実際に試してみることも可能です。下記のコマンドでは0x41 0x42 0x43(10進で65, 66,67)を出力しています。
結果はASCIIコード表どおりA B Cとなります。
| |
ANSI エスケープシーケンス
上記の方法で文字を出力できることはわかりました。さらにそれに色を付ける方法があります。 ANSI エスケープシーケンスと呼ばれているものを付加します。 まず最初にANSIエスケープシーケンスを利用したコマンドとその結果を示します。
| |
上記だと色がわからないと思うのでキャプチャを貼ります。白背景に赤文字になりました。

上記のコマンド例での \x1bがエスケープコードと言われるものです。
16進数で0x1b、8進だと033です。このエスケープコードがエスケープシーケンスの始まりを示します。
上記の例だと\x1b\[31;47m と \x1b\[0m がエスケープシーケンスです。
エスケープコードに続く [ は CSI (Control Sequence Introducer)と呼ばれるもので制御シーケンスの始まりを示します。
エスケープシーケンスのうち \[31;47m と \[0m がそれに該当します。
また制御シーケンスが mで終わる場合は SGR (Select Graphic Rendition)、表示様式選択とよばれます。
これを使って文字と背景色を設定しています。\[31;47mのうち 31が文字色を設定しているところで赤を表します。
47が背景色を設定しているところで白を表します。ココの値をかえると色を変更できます。
ちなみに2つ目のSGR の 0はリセットです。これがないとずっと色設定をしたままになってしまいます。 わかりづらいとおもうので上記の色付きで出力するコマンドをちょっと書き換えると下記のようになります。
| |
色設定についてはWikipediaのANSI_escape_code - 3-bit and 4-bitの表を参照してください。 FGが文字色、BGが背景色です。
ちなみにですがエスケープコードは \eと書くこともできます。どちらでも同じ意味です。
| |
補足
もうちょっと詳しく知りたい方は碧色工房 - ANSIエスケープコードが参考になるかと思います。 簡単なC言語で説明してあります。
さらに詳しく知りたい人は規格を確認してください。色以外にもいろんなことができます。規格はISO/IEC 6429 です。 その日本語訳がJISで定められています。JIS X0211です。 JIS検索から X0211で検索すれば日本語のPDFが閲覧できます(ダウンロードとか印刷はNGらしい)。
コマンド出力の確認
色付き文字で出力するコマンドも上記のANSI
エスケープシーケンスを使っています。それを確認してみたいと思います。
ためしに ls コマンドを色付きで出力してみます。下記のようになりますね。ちなみに最初のバックスラッシュは素の ls を実行するためです。これがないと .bashrc などで設定されたエイリアスを実行することがあるためつけています。

次にこの実行結果をlessに渡してみましょう。
下記のようにリダイレクションすればOKです。
このとき --color=always は必ずつけてください。理由は後で述べます。
| |
注目してほしいのは ESCとあるところです。これが前述したANSIエスケープシーケンスです。
よく出てくるESC\[01;34m について説明すると 01は太字、34は青色を指定しています。
ちなみに --color=alwaysをつけない場合(デフォルト)は、パイプの場合エスケープシーケンスは出力されません。
lsが気を遣って出力しません。lessもそうでしたが、パイプ先がエスケープシーケンスを扱えるとは限らないためです。
補足ですが catはエスケープシーケンスを扱えます。
下記のようにしてみてください。色付きで出力されるはずです。
| |
ANSIエスケープシーケンスをHTMLに変換する
色付きのコマンド出力は ANSI
エスケープシーケンスを使っていることを説明しました。これを色付きのまま保存するにはHTMLに変換するのが楽ちんです。HTMLならブラウザで簡単に見れますしね。
ここではansi2html というコマンドを使います。
ansi2html
インストールは Ubuntu なら apt install でできます。
手元のUbuntu20.04だと下記のコマンドでインストールできました。
ansi2html と打ってみるとどうやってインストールするか教えてくれると思います。
| |
使い方は簡単です。下記のようにパイプでANSIエスケープのログを渡すだけでOKです。HTMLが出力されますので任意の名前でファイルの保存すればOKです。
| |
下記は出力したHTMLをブラウザで表示しています。ちゃんと色も出力されていますね。期待通りです。

特に使い方で迷うことはないと思いますが、詳細はansi2htmlを参照してください。
ページャーとバッファリング
lsの場合は上記の方法でOKです。次はもう少し複雑な例を見てみます。
例として repo というツールの出力を考えます。
repo は Gitのフロントエンドで、
数百以上のリポジトリからなるAndoroid AOSPのようなプロジェクトを扱うためのツールです。
例えば下記のように実行すれば色付きでandroid-12.1.0_r2 のバージョンと現在のバージョの差分を出力してくれます。
| |
出力はこんな感じです。

この出力を色付きのままHTMLに保存することを考えます。
unbuffer
repo diffmanifests コマンドもデフォルトの lsと同じように標準出力以外への出力の際はANSIエスケープシーケンスを出力しません。
色なしになります。例えば下記のようにパイプを使うと色なしの出力になります。
| |

ls の場合は出力先が標準出力以外でも常に色付きで出力するオプション --color=always がありました。
しかし repo diffmanifestsにはありません。
そのためさらに別のツールが必要になります。
unbuffer というツールがそれを可能にします。インストールはUbuntuの場合は下記のようにすればOKです。
| |
使い方も簡単です。unbuffer に色付きで出力したいコマンドを渡すだけです。
| |
このように色付きで出力されるようになります。

ページャー
上記で色付きで出力できるようになったので下記のようにリダイレクションで結果をファイルに保存したとします。
| |
残念ながらこのままでは動作しません。待てど暮せどコマンドが終了しないはずです。
実はless が起動してしまっておりコマンド待の状態になっているためです。
lessなのでqを押すと終了します。 ここでは一気に全部出力してほしいのでless でなく cat を使うようにすればOKです。
その設定は環境変数 PAGERで指定できます。ページャーと呼ばれる設定で、下記のように PAGER に catを設定して実行すればOKです。
| |
これで色付き強制のオプションのないコマンドでも色付きで出力を保存できるようになりました。 出力したHTML をブラウザで開くと色付きで表示されます。
まとめ
今回は最初に文字に色を付けるためのANSIエスケープシーケンスについて簡単に説明しました。
そしてコマンドの出力を色付きで保存する方法を説明しました。基本はansi2htmlコマンドに渡してHTMLにすればOKです。
リダイレクションするとANSIエスケープシーケンスを抑止して色なしになってしまうコマンドではunbuffer をさらに使えばOKです。
ページャーが less 担っている場合は PAGER環境変数を cat にすればOKです。
この方法は大抵のコマンドで使えると思います。いろんなコマンドで試してみてください。