みなさんも生きていればコマンドの出力をブログに貼り付けたいということがあるでしょう. というわけでコマンド出力をなんとか HTML にして貼り付ける方法のご紹介です.
TL; DR: script -q /dev/null <command> | ansi2html -i | pbcopy
まず HTML は基本的には以下のような形で良いはずです. 色とかフォントとかは適当に変えたければ変える.
<pre style="color:#CCC;background-color:#000"> <!-- ここにコマンドの出力を貼り付ける --> </pre>
例えば ls
の結果を貼り付けたいとすると, 適当に pbcopy
に pipe で流し込んでコピーすれば良さそうに思われます.
この pbcopy
は macOS に標準で存在するコマンドで, 標準入力から受け取った内容をクリップボードにコピーします.
他の環境の場合は適当に置き換えて読んでください.
ls | pbcopy
これを貼り付けて完成. めでたし.
LICENSE README.md bin coverage jest.config.js lib node_modules package.json yarn-error.log yarn.lock
で, 終わりではなくて, (ひょっとしたら環境によるかもしれないけれど) 普通に ls
を実行したとき, こんな風に 1 行に 1 個のファイル / ディレクトリの形で出力されたでしょうか?
おそらく以下のように, 1 行に複数個のファイル / ディレクトリが含まれる形になるのではないかと思います.
LICENSE README.md bin coverage jest.config.js lib node_modules package.json yarn-error.log yarn.lock
なぜ出力が異なるかと言うと, コマンドは自身の出力が TTY 端末に対して行われているかを判別することができて, ls
などコマンドによってはこれを見て出力を切り替えているのです.
例えば Node.js では以下のようなコードで判別できます.
出力が TTY 端末でない場合 (例えば pipe) に対して行われている場合は falsy な値 (undefined
) が得られます.
$ node -e 'console.log(process.stdout.isTTY)'
true
$ node -e 'console.log(process.stdout.isTTY)' | cat
undefined
では TTY 端末に出力される結果をそのまま取得したい場合はどうしたら良いかというと, script コマンドを使ったトリックを使います.
例えば実行したいコマンドが foo bar
とすると, 以下のように script
経由でコマンドを実行します.
# macOS の場合 script -q /dev/null foo bar # Linux の場合はこうっぽい script --return -qfc /dev/null 'foo bar'
script
は本来は対話的なコマンドの実行を記録するためのコマンドで, 通常は記録の出力先ファイルを指定するところを, ここでは記録は必要ないので /dev/null
に捨てています.
重要なのは script
経由でコマンドを実行すると, たとえ script
の出力が pipe などであったとしても, コマンド自身は TTY 端末に出力していると勘違いするような形で実行されるという点です.
実際, Node.js で判別するコードを使って試してみると, 次のような結果が得られます.
$ script -q /dev/null node -e 'console.log(process.stdout.isTTY)' | cat
true
ちなみにこの方法は昔色々ぐぐっていたところ bs-loader の PR で使われているのから見つけました.
さて元の話題に戻って, ls
でやってみましょう.
script -q /dev/null ls | pbcopy
LICENSE README.md bin coverage jest.config.js lib node_modules package.json yarn-error.log yarn.lock
ババーン.
ところで色付けたくないですか?
ls
には -G
オプションがあり, これが有効な場合はファイル / ディレクトリや権限の違いなどがわかる形で色付けして出力してくれます.
というわけでやってみましょう.
script -q /dev/null ls -G | pbcopy
LICENSE README.md [34mbin[39;49m[0m [34mcoverage[39;49m[0m jest.config.js [34mlib[39;49m[0m [34mnode_modules[39;49m[0m package.json yarn-error.log yarn.lock
残念ながら, なんか [34m
とか出てしまっています.
これは ANSI escape code というやつで, この場合は要するに出力に色を付けるためのものが, 生のまま見えてしまっている形です.
これを HTML に変換してみましょう. 変換のためのツールは色々あるようですが, ちょっと調べた感じでは ansi2html というのが良い感じでした.
適当にインストールして,
pip install ansi2html
以下のように pbcopy
の前に挟みます.
-i
は HTML の style をインラインの属性として出力するオプションで, -s solarized
はカラースキーマを指定しています.
詳しくはansi2html --help
を見ましょう.
script -q /dev/null ls -G | ansi2html -i -s solarized | pbcopy
結果はこんな感じ. 無事色がついてめでたいですね.
LICENSE README.md bin coverage jest.config.js lib node_modules package.json yarn-error.log yarn.lock