LinuxやWSLなどでコマンドを実行した時、出力がカラーで表示されることがあると思います。たとえば ls --color=auto
とかですね。このような出力を普通にリダイレクションなどでファイルに書き込むと色情報が失われて普通の文字になってしまいます。ls だったら別にそれで困らないと思いますが、色がないと分かりづらいdiff のようなコマンのド出力だと色情報ごと保存したいときもあると思います。そのような場合において今回はコマンド出力をHTMLに変換して色ごと保存する方法を説明します。
色付き文字を出力する方法
まず最初に基礎知識として文字に色を付ける方法を簡単に説明します。ANSI エスケープシーケンスを使います。そんなの知っているよ!というかたは飛ばしてOKです。
ASCII コード
通常文字(アルファベットと記号と数字。いったん日本語文字は置いておいてください)を出力をするにはASCIIコードを用います。CPUはけっきょく数値しか扱えないので文字を表現しようとすると数値を文字に対応させる必要があります。その対応表がASCIIコード表と呼ばれるものです。たとえば「A」は65、「B」は66、「C」は67…. となっています。実際のASCIIコード表はググれば出てきます。参考までに Wikipedia のリンクを貼っておきます。いろいろむずかしいこと書かれていますが、要は「ASCII印字可能文字」のところの表に合わせて数字を文字として解釈するよ、というだけの話です。
実際に試してみることも可能です。下記のコマンドでは0x41 0x42 0x43(10進で65, 66, 67)を出力しています。結果はASCIIコード表どおり「A B C」となります。
$ echo -e "\x41 \x42 \x43" A B C
ANSI エスケープシーケンス
上記の方法で文字を出力できることはわかりました。さらにそれに色を付ける方法があります。ANSI エスケープシーケンスと呼ばれているものを付加します。まず最初にANSIエスケープシーケンスを利用したコマンドとその結果を示します。
$ echo -e "\x1b[31;47m\x41 \x42 \x43\x1b[0m" A B C
上記だと色がわからないと思うのでキャプチャを貼ります。白背景に赤文字になりました。
上記のコマンド例での \x1b がエスケープコードと言われるものです。16進数で0x1b、8進だと033です。このエスケープコードがエスケープシーケンスの始まりを示します。上記の例だと \x1b[31;47m と \x1b[0m がエスケープシーケンスです。
エスケープコードに続く [
は Control Sequence Introducer, CSI と呼ばれるもので制御シーケンスの始まりを示します。エスケープシーケンスのうち [31;47m と [0m がそれに該当します。また制御シーケンスが m で終わる場合は Select Graphic Rendition, SGR、表示様式選択とよばれます。これを使って文字と背景色を設定しています。[31;47m のうち 31 が文字色を設定しているところで赤を表します。47が背景色を設定しているところで白を表します。ココの値をかえると色を変更できます。ちなみに2つ目の SGR の 0 はリセットです。これがないとずっと色設定をしたままになってしまいます。
分かりづらいとおもうので上記の色付きで出力するコマンドをちょっと書き換えると下記のようになります。
$ echo -e "\x1b[色設定;色設定m <色付きで出力したい文字列> \x1b[0m"
色設定については下記の Wikipediaの下記の表を参照してください(英語ですけど色でわかると思います)。FGが文字色、BGが背景色です。
ちなみにですがエスケープコードは \e と書くこともできます。どちらでも同じ意味です。
$ echo -e "\e[31;47m\x41 \x42 \x43\e[0m"
補足
もうちょっと詳しく知りたい方は下記のサイトが参考になるかと思います。簡単なC言語で説明してあります。
更に詳しく知りたい人は規格を確認してください。色以外にも色んな事ができます。規格はISO/IEC 6429 です。その日本語訳がJISで定められています。JIS X0211です。下記のJISのサイトから X0211 で検索すれば日本語のPDFが閲覧できます(ダウンロードとか印刷はNGらしい)。
コマンド出力の確認
色付き文字で出力するコマンドも上記のANSI エスケープシーケンスを使っています。それを確認してみたいと思います。
ためしに ls コマンドを色付きで出力してみます。下記のようになりますね。ちなみに最初のバックスラッシュは素の ls を実行するためです。これがないと .bashrc などで設定されたエイリアスを実行することがあるためつけています。
次にこの実行結果をlessに渡してみましょう。下記のようにリダイレクションすればOKです。このとき –color=alway は必ずつけてください。理由は後で述べます。
$ \ls -laF --color=always | \less 合計 128 drwxrwxr-x 29 shinya shinya 4096 3月 30 01:29 ESC[0mESC[01;34m.ESC[0m/ drwxr-xr-x 14 shinya shinya 4096 3月 2 22:05 ESC[01;34m..ESC[0m/ drwxrwxr-x 2 shinya shinya 4096 6月 18 2021 ESC[01;34m.bootstrapESC[0m/ drwxrwxr-x 7 shinya shinya 4096 6月 18 2021 ESC[01;34m.repoESC[0m/ lrwxrwxrwx 1 shinya shinya 19 6月 26 2021 ESC[01;36mAndroid.bpESC[0m -> build/soong/root.bp lrwxrwxrwx 1 shinya shinya 23 6月 26 2021 ESC[01;36mBUILDESC[0m -> build/bazel/bazel.BUILD lrwxrwxrwx 1 shinya shinya 27 6月 26 2021 ESC[01;36mWORKSPACEESC[0m -> build/bazel/bazel.WORKSPACE -rw-rw-r-- 1 shinya shinya 2607 3月 30 01:20 a.txt drwxrwxr-x 38 shinya shinya 4096 9月 30 23:41 ESC[01;34martESC[0m/ drwxrwxr-x 15 shinya shinya 4096 9月 30 23:41 ESC[01;34mbionicESC[0m/ drwxrwxr-x 4 shinya shinya 4096 6月 26 2021 ESC[01;34mbootableESC[0m/ lrwxrwxrwx 1 shinya shinya 26 6月 26 2021 ESC[01;36mbootstrap.bashESC[0m -> ESC[01;32mbuild/soong/bootstrap.bashESC[0m* drwxrwxr-x 8 shinya shinya 4096 9月 30 23:41 ESC[01;34mbuildESC[0m/ drwxrwxr-x 3 shinya shinya 4096 6月 26 2021 ESC[01;34mcompatibilityESC[0m/ drwxrwxr-x 14 shinya shinya 4096 6月 26 2021 ESC[01;34mctsESC[0m/ drwxrwxr-x 8 shinya shinya 4096 9月 30 23:41 ESC[01;34mdalvikESC[0m/ drwxrwxr-x 5 shinya shinya 4096 6月 26 2021 ESC[01;34mdevelopersESC[0m/ drwxrwxr-x 21 shinya shinya 4096 9月 30 23:41 ESC[01;34mdevelopmentESC[0m/ drwxrwxr-x 11 shinya shinya 4096 6月 26 2021 ESC[01;34mdeviceESC[0m/ drwxrwxr-x 347 shinya shinya 12288 9月 30 23:41 ESC[01;34mexternalESC[0m/ drwxrwxr-x 16 shinya shinya 4096 6月 26 2021 ESC[01;34mframeworksESC[0m/ -rw-rw-r-- 1 shinya shinya 0 3月 29 20:44 fuga.html drwxrwxr-x 15 shinya shinya 4096 6月 26 2021 ESC[01;34mhardwareESC[0m/ drwxrwxr-x 5 shinya shinya 4096 6月 26 2021 ESC[01;34mkernelESC[0m/ drwxrwxr-x 20 shinya shinya 4096 9月 30 23:41 ESC[01;34mlibcoreESC[0m/ drwxrwxr-x 10 shinya shinya 4096 6月 26 2021 ESC[01;34mlibnativehelperESC[0m/ -rw-rw-r-- 1 shinya shinya 0 3月 30 01:29 ls_color.txt drwxrwxr-x 9 shinya shinya 4096 6月 26 2021 ESC[01;34mpackagesESC[0m/ drwxrwxr-x 6 shinya shinya 4096 6月 26 2021 ESC[01;34mpdkESC[0m/ drwxrwxr-x 10 shinya shinya 4096 6月 26 2021 ESC[01;34mplatform_testingESC[0m/ drwxrwxr-x 32 shinya shinya 4096 9月 30 23:41 ESC[01;34mprebuiltsESC[0m/ drwxrwxr-x 22 shinya shinya 4096 6月 26 2021 ESC[01;34msdkESC[0m/ drwxrwxr-x 46 shinya shinya 4096 7月 30 2021 ESC[01;34msystemESC[0m/ drwxrwxr-x 11 shinya shinya 4096 6月 26 2021 ESC[01;34mtestESC[0m/ drwxrwxr-x 4 shinya shinya 4096 6月 26 2021 ESC[01;34mtoolchainESC[0m/ drwxrwxr-x 24 shinya shinya 4096 6月 26 2021 ESC[01;34mtoolsESC[0m/
注目してほしいのは「ESC」とあるところです。これが前述したANSIエスケープシーケンスです。よく出てくる ESC[01;34m について説明すると 01は太字、34は青色を指定しています。
ちなみに –color=alway をつけない場合(デフォルト)は、パイプの場合エスケープシーケンスは出力されません。ls が気を遣って出力しません。less もそうでしたがパイプ先がエスケープシーケンスを扱えるとは限らないためです。
ちなみに cat はエスケープシーケンスを扱えます。下記のようにしてみてください。色付きで出力されるはずです。
$ \ls -laF --color=always | cat
ANSIエスケープシーケンスをHTMLに変換する
色付きのコマンド出力は ANSI エスケープシーケンスを使っていることがを説明しました。これを色付きのまま保存するにはHTMLに変換するのが楽ちんです。HTMLならブラウザで簡単に見れますしね。ここでは ansi2html というコマンドを使います。
ansi2html
インストールは Ubuntu なら apt install でできます。手元の Ubuntu20.04だと下記のコマンドでインストールできました。ansi2html と打ってみるとどうやってインストールするか教えてくれると思います。
$ sudo apt install colorized-logs
使い方は簡単です。下記のようにパイプでANSIエスケープのログを渡すだけでOKです。HTMLが出力されますので任意の名前でファイルの保存すればOKです。
$ \ls -laF --color=always | ansi2html > ls_color.html
下記は出力したHTMLをブラウザで表示しています。ちゃんと色も出力されていますね。期待通りです。
特に使い方で迷うことはないと思いますが、詳細は公式サイトを参照してください。
ページャーとバッファリング
ls の場合は上記の方法でOKです。次はもう少し複雑な例を見てみます。例として repo というツールの出力を考えます。
repo は Git のフロントエンドで、数百以上のリポジトリからなる Andoroid AOSP のようなプロジェクトを扱うためのツールです。例えば下記のように実行すれば色付きで android-12.1.0_r2 のバージョンと現在のバージョの差分を出力してくれます。
$ repo diffmanifests android-12.1.0_r2.xml default.xml
こんな感じです。
この出力を色付きのままHTMLに保存することを考えます。
unbuffer
repo diffmanifests コマンドもデフォルトの ls と同じように標準出力以外への出力の際はANSIエスケープシーケンスを出力しません。色なしになります。例えば下記のようにパイプを使うと色なしの出力になります。
$ repo diffmanifests android-12.1.0_r2.xml default.xml | cat
ls の場合は出力先が標準出力以外でも常に色付きで出力するオプション –color=always がありました。しかし repo diffmanifests にはありません。そのためさらに別のツールが必要になります。unbuffer というツールがそれを可能にします。インストールはUbuntuの場合は下記のようにすればOKです。
$ sudo apt install expect
使い方も簡単です。unbuffer に色付きで出力したいコマンドを渡すだけです。
$ unbuffer -p repo diffmanifests android-12.1.0_r2.xml default.xml | cat
このように色付きで出力されるようになります。
ページャー
上記で色付きで出力できるようになったので下記のようにリダイレクションで結果をファイルに保存したとします。
$ unbuffer -p repo diffmanifests android-12.1.0_r2.xml default.xml | ansi2html > diffmanifests.html
残念ながらこのままでは動作しません。待てど暮せどコマンドが終了しないはずです。実は less が起動してしまっておりコマンド待の状態になっているためです。less なので「q」を押すと終了します。
ここでは一気に全部出力してほしいので less でなく cat を使うようにすればOKです。その設定は環境変数 PAGER で指定できます。ページャーと呼ばれる設定で、下記のように PAGER に cat を設定して実行すればOKです。
$ PAGER=cat unbuffer -p repo diffmanifests android-12.1.0_r2.xml default.xml | ansi2html > diffmanifests.html
これで色付き強制のオプションのないコマンドでも色付きで出力を保存できるようになりました。出力した HTML をブラウザで開くと色付きで表示されます。
まとめ
今回は最初に文字に色を付けるためのANSI エスケープシーケンスについて簡単に説明しました。
そしてコマンドの出力を色付きで保存する方法を説明しました。基本は ansi2html コマンドに渡してHTMLにすればOKです。リダイレクションするとANSIエスケープシーケンスを抑止して色なしになってしまうコマンドでは unbuffer をさらに使えばOKです。ページャーが less 担っている場合は PAGER 環境変数を cat にすればOKです。
この方法は大抵のコマンドで使えると思います。色んなコマンドで試してみてください。
コメント