ANSIエスケープシーケンスと cowthink のメモです。(お遊びメモです)
ANSIエスケープシーケンス
ANSIエスケープシーケンスは、文字色を変えたりできる画面制御用シーケンスコードです。
解説はここでは割愛します。(ネットにはたくさん解説がありますので)
cowthink
cowthink は アスキーアートを出力するコマンドです。cowsay に含まれます。
僕は、以下の投稿で知りました。
(Ubuntu Linux の場合の) 上の投稿で紹介されているコマンドのインストール方法です。
$ sudo apt-get install cowsay # cowsay (cowthink が含まれるパッケージ)
$ sudo apt-get install sysvbanner # banner
$ sudo apt-get install figlet # figlet
$ sudo apt-get install toilet # toilet
で、この cowthink を動かしてみたいなぁ、と前から思っていたのでやってみました。
yoko-scroll.rb
cowthink を動かしているスクリプトです。
# 使い方の例
$ cowthing Hello | ruby ./yoko-scroll.rb
trap(:INT) { exit 0 }
COLS, RAWS = 80, 24
TOP = 1 + 1
lines = $<.readlines.first(RAWS).map(&:chomp) + ['']
maxlen = lines.map(&:size).max
lines = lines.map{|s| (' ' * COLS) + s.ljust(maxlen + 1) }
cls = -> { "\e[2J" }
move = -> y { "\e[#{y}H" }
interval = -> { sleep 0.1 }
lines.first.size.times do |i|
print cls.() + move.(TOP)
puts lines.map {|s| s[i, COLS]} * $/
interval.()
end
(もちろん cowthink 以外でも)標準入力、あるいは引数で指定したファイルの内容を横にスクロールさせながら表示します。
適当な作りで不足したところも多々ありますが、お遊びのものなので、これ以上はメンテしません。
悪しからず。
cowthink のメッセージに色をつける
上のスクリプトとは関係ありませんが、cowthink のメッセージに緑色をつけようと思いました。
echo に解釈させるのと同じように「\e[32m...\e[m」でくるんでみましたが。。。
$ cowthink "\e[32mHello\e[m" # これでは、うまくいかない。。。。
うーん、残念ダメでした。
。。。と諦めてしまっては、そこで試合終了です。
以下のようにすると、うまくいきました。「\e」の部分2箇所を変えています。
表示上は「^[」となっていますが、実際はこの部分は生の「ESC」を入力しています。
大抵の Unix 系のターミナルでは、生コードの入力は「Ctrl-V」に続けてそのコードを入力します。
(Ctrl-V は、プログラミング言語での、文字列リテラル内で次に続く特殊な文字の意味を打ち消す「\」のようなものと思ってください)
[Ctrl] + 'V', [ESC] # [Ctrl]キーと「V」キーを同時に押して、続けて「ESC」キーを押す
「^[」 という表示は、「Ctrl + '['」をあらわしています。
Ctrl は上位3ビットをマスクするので、以下のようになります。
0x5b (ASCII '[') 0101 1011
Ctrl 修飾 & 0001 1111 # Ctrl は上位3ビットをマスクする
------------------------------
0x1b (ASCII ESC) 0001 1011
なので、「Ctrl-V, [ESC]」は「Ctrl-V, Ctrl-[」と入力しても同じです。
Ctrl-[ : 0x5b (ASCII '[') & 0x1f (Ctrl) => 0x1b (ASCII ESC)
Ctrl-H : 0x48 (ASCII 'H') & 0x1f (Ctrl) => 0x08 (ASCII BS 後退(バックスペース))
Ctrl-I : 0x49 (ASCII 'I') & 0x1f (Ctrl) => 0x09 (ASCII HT 水平タブ)
Ctrl-M : 0x4d (ASCII 'M') & 0x1f (Ctrl) => 0x0d (ASCII CR 復帰(リターン))
制御コードが送られて実際にどう処理するかは、ドライバやアプリ次第です。
ちなみに、上の yoko-scroll.rb では、入力に制御コードが含まれるかどうかなどは全然対応していません。悪しからず。
ansi_escape_sequence.rb
余談。
関連するスクリプトを掲載しておきます。
というか、ディスクの隅にこのスクリプトを見つけたのがこのメモの発端です。
多分、これを作るときの実験コードだったと思います。
# !/usr/bin/env ruby
# -*- coding: UTF-8 -*-
$KCODE = 'UTF-8' if RUBY_VERSION < '1.9'
module ANSI
module EscapeSequence
extend self
def esc(ctl, *ss)
"\e[#{ss.join ';'}#{ctl}"
end
def move(y, x=1) ; esc:H, y, x ; end
def home ; esc:H ; end
def up(y=1) ; esc:A, y ; end
def down(y=1) ; esc:B, y ; end
def right(x=1) ; esc:C, x ; end
def left(x=1) ; esc:D, x ; end
def cls(mode=2) ; esc:J, mode ; end
def insert(n=1) ; esc:L, n ; end
def delete(n=1) ; esc:M, n ; end
def del(mode=2) ; esc:K, mode ; end
def save ; esc:s ; end
def undo ; esc:u ; end
def cursor_on ; esc:l, '5>' ; end
def cursor_off ; esc:h, '5>' ; end
def color(s0, *ss)
seq = ([s0] + ss).map do |s|
{ Reset: '',
Bold: 1,
Underline: 4,
Reverse: 7,
Black: 30,
Red: 31,
Green: 32,
Yellow: 33,
Blue: 34,
Magenta: 35,
Cyan: 36,
White: 37,
BBlack: 40,
BRed: 41,
BGreen: 42,
BYellow: 43,
BBlue: 44,
BMagenta: 45,
BCyan: 46,
BWhite: 47,
}[s] || s
end
esc:m, *seq
end
def wrap(s, *ss)
color(*ss) + s + reset
end
alias :seq :wrap
def reset ; color:Reset ; end
def bold ; color:Bold ; end
def underline ; color:Underline ; end
def reverse ; color:Reverse ; end
def black ; color:Black ; end
def red ; color:Red ; end
def green ; color:Green ; end
def yellow ; color:Yellow ; end
def blue ; color:Blue ; end
def magenta ; color:Magenta ; end
def cyan ; color:Cyan ; end
def while ; color:White ; end
def bblack ; color:BBlack ; end
def bred ; color:BRed ; end
def bgreen ; color:BGreen ; end
def byellow ; color:BYellow ; end
def bblue ; color:BBlue ; end
def bmagenta ; color:BMagenta ; end
def bcyan ; color:BCyan ; end
def bwhile ; color:BWhite ; end
end
end
if __FILE__ == $0
ESC = ANSI::EscapeSequence
p ESC.move 1, 3 #=> "\e[1;3H"
p ESC.home #=> "\e[H"
p ESC.cls 1 #=> "\e[1J"
p ESC.cls #=> "\e[2J"
p ESC.underline #=> "\e[4m"
p ESC.red #=> "\e[31m"
p ESC.bcyan #=> "\e[46m"
p ESC.color :Bold, :Green #=> "\e[1;32m"
p ESC.seq "hello", :Underline, :BBlue #=> "\e[4;44mhello\e[m"
end
# vi:set ts=2 sw=2 et:
おわりに
このメモのスクリプトなどの動作は以下の環境で行っております。
- Ubuntu Linux 14.04
- Ruby 2.1.5
エビデンス取るのにはいつも時間がかかりますが、今回アニメーションgif作るのにはスクリプトの数倍の時間がかかりました。。。