ruby ことはじめ - 第6回「人生いろいろ、ハッシュで色々♪」
こんばんは。ほぼ毎日行っているスポーツジムが休みのため、毎週火曜日は自宅近くの温泉に行くkyagiです。おかげで、自宅のお風呂には数えるほどしか入ったことがありません。:-)
今回はrubyを使ってターミナルのカラー出力をやってみよう、というお話です。というのも、先日お仕事で作成したスクリプトの出力をデモしてみせたところ「これじゃOKかNGかパッと見、わからないなあ」と言われたことがキッカケだったりします。
「一目瞭然な様にカスタマイズしちゃる!」と心に決めたkyagiは今までスクリプト内で使用していた平凡なprintをやめ、独自でモジュールを作成することにしました。ターミナルへの出力はLinuxのデーモンを |start|stop| した時のようにエスケープシーケンスを使ってOKをグリーンでNGをレッドでカラー表示にしてやろう、という魂胆です。データ構造としてはハッシュを使い、キーで色指定できるようにしてみました。
実はrubyでは「全てがオブジェクト」のためハッシュのキーにシンボルをそのまま使用することはできません。kyagiはいちいちキーをStringオブジェクトとして""で囲むのがメンドクサイのでキーにシンボルを使用し:symbolというふうに書くことにしました。
# ruby
× h = { apple => 120, banana => 30 }; print h[apple];
○ h = { "apple" => 120, "banana" => 30 }; print h["apple"];
○ h = { :apple => 120, :banana > 30 }; print h[:apple];
# perl
○ %h = (apple => 90, banana => 30); print $h{apple}
モジュールの名前をMessage.rbとし、スクリプト全体で使うことを考慮し、汎用的なモジュールとして設計します。後は使いたいクラスの先頭で include すればよいだけです。
#
# = Message.rb: Framework Message Module
#
# Copyright (C) 2007, Miracle Linux Corporation
# Author:: kyagi
# Documentation::
#
module Message
Color = { :def => "", :red => "\033[0;31m", :green => "\033[0;32m",
:brown => "\033[0;33m", :blue => "\033[0;34m",
:purple => "\033[0;35m", :cyan => "\033[0;36m",
:gray => "\033[0;37m" }
def msg(str, color=:def)
if (color == :def)
suffix = ""
else
suffix = "\033[0m"
end
STDOUT.print Color[color] + str + suffix
STDOUT.flush
end
def msg_wrn(str)
STDOUT.puts "WARNING: " + str
STDOUT.flush
end
def msg_err(str)
STDERR.puts "ERROR: " + str
STDOUT.flush
end
end
ワンライナでテストした結果は以下になります。ちゃんと動いていますね:-)
カラー出力はシェルでも応用が効きそうだったので cecho(Color ECHO)としてシェル版も作成してみました。とりあえず自作のシェル関数のコレクションであるfunctionsというファイルに入れておきます。
cecho ()
{
local MSG=$1
local COLOR=$2
local PREFIX=
local SUFFIX="\033[0m"
case "$COLOR" in
red)
PREFIX="\033[0;31m"
;;
green)
PREFIX="\033[0;32m"
;;
brown)
PREFIX="\033[0;33m"
;;
blue)
PREFIX="\033[0;34m"
;;
purple)
PREFIX="\033[0;35m"
;;
cyan)
PREFIX="\033[0;36m"
;;
gray)
PREFIX="\033[0;37m"
;;
*)
PREFIX=""
SUFFIX=""
;;
esac
MSG="$PREFIX$MSG$SUFFIX"
echo -e "$MSG"
}
としているうちに、最近になってシステム管理者からftpサーバのテストを頼まれました。テストをしたのはいいのですが、下記のログを提出する際に自分の打ったコマンドだけをカラー出力するパーサも同梱するとシステム管理者に喜ばれるかもしれないと思い、作成してみることにしました。古今東西、泣く子とシステム管理者に逆らえません&機嫌を取って悪いことはありません。:-)ログは下記のようになっています。
kyagi@dhcp-0225:~/new_ftp_test$ ping -c3 219.118.163.67
PING 219.118.163.67 (219.118.163.67) 56(84) bytes of data.
64 bytes from 219.118.163.67: icmp_seq=1 ttl=254 time=0.368 ms
64 bytes from 219.118.163.67: icmp_seq=2 ttl=254 time=0.375 ms
64 bytes from 219.118.163.67: icmp_seq=3 ttl=254 time=0.388 ms
--- 219.118.163.67 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.368/0.377/0.388/0.008 ms
kyagi@dhcp-0225:~/new_ftp_test$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
10.1.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
0.0.0.0 10.1.0.1 0.0.0.0 UG 0 0 0 eth0
kyagi@dhcp-0225:~/new_ftp_test$ nslookup ftp.miraclelinux.com
Server: 10.1.0.21
Address: 10.1.0.21#53
ftp.miraclelinux.com canonical name = ftp01.miraclelinux.com.
Name: ftp01.miraclelinux.com
Address: 219.118.163.67
kyagi@dhcp-0225:~/new_ftp_test$ nslookup 219.118.163.67
Server: 10.1.0.21
Address: 10.1.0.21#53
Non-authoritative answer:
67.163.118.219.in-addr.arpa canonical name = 67.64.163.118.219.in-addr.arpa.
67.64.163.118.219.in-addr.arpa name = ftp01.miraclelinux.com.
Authoritative answers can be found from:
64.163.118.219.in-addr.arpa nameserver = ns.miraclelinux.com.
64.163.118.219.in-addr.arpa nameserver = ns1.bit-drive.ne.jp.
64.163.118.219.in-addr.arpa nameserver = ftp01.miraclelinux.com.
ns.miraclelinux.com internet address = 219.118.163.66
ns1.bit-drive.ne.jp internet address = 211.9.32.227
ftp01.miraclelinux.com internet address = 219.118.163.67
kyagi@dhcp-0225:~/new_ftp_test$ ftp -p 10.0.0.5
Connected to 10.0.0.5.
220 MIRACLE LINUX FTP Server
Name (10.0.0.5:kyagi): anonymous
331 Anonymous login ok, send your complete email address as your password.
Password:
230 Anonymous login ok, restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd pub/Miracle/3.0/updates/RPMS/
250 CWD command successful
ftp> ls Auto*
227 Entering Passive Mode (10,0,0,5,135,23).
150 Opening ASCII mode data connection for file list
-rw-r--r-- 1 ftpadm ftpadm 205605 Feb 17 04:55 AutoConfig-1-17A.i386.rpm
226 Transfer complete.
ftp> bin
200 Type set to I
ftp> put B_1_1.png
local: B_1_1.png remote: B_1_1.png
227 Entering Passive Mode (10,0,0,5,135,24).
550 B_1_1.png: Permission denied
ftp> quit
221
kyagi@dhcp-0225:~/new_ftp_test$
このログを自分の打ったコマンドだけがカラー表示になるように、上記のシェル関数 cecho と less -R を使用して readlog というスクリプトを作成してみました。こういったスクリプトは作業ログを確認するのにも一役買うのではないかと思います。
#!/bin/sh
##
## Read test result log with color
##
## Author: kyagi
## Usage : readlog test.log
##
. functions
LOG=$1
MYPROMPT="kyagi@dhcp-0225:~/new_ftp_test\\$"
while read LINE; do
echo "$LINE" | grep -q "$MYPROMPT"
if [ $? == 0 ]; then
echo -n "$LINE" | awk -F"$" '{printf("%s$",$1)}'
LINE_COLOR=`echo "$LINE" | awk -F"$" '{print $2}'`
cecho "$LINE_COLOR" red
else
echo "$LINE"
fi
done <"$LOG" | less -R -N
実験したところ、このままだと ftp セッションに入った時のカラーが色分けされていないのが気になったので、下記のパッチのように拡張してみました(その場しのぎですが)。
$ diff -c readlog readlog_with_ftp
*** readlog 2007-02-28 01:36:14.000000000 +0900
--- readlog_with_ftp 2007-02-28 01:36:37.000000000 +0900
***************
*** 10,15 ****
--- 10,16 ----
LOG=$1
MYPROMPT="kyagi@dhcp-0225:~/new_ftp_test\\$"
+ FTPPROMPT="ftp>"
while read LINE; do
echo "$LINE" | grep -q "$MYPROMPT"
if [ $? == 0 ]; then
***************
*** 17,22 ****
LINE_COLOR=`echo "$LINE" | awk -F"$" '{print $2}'`
cecho "$LINE_COLOR" red
else
! echo "$LINE"
fi
done <"$LOG" | less -R -N
--- 18,30 ----
LINE_COLOR=`echo "$LINE" | awk -F"$" '{print $2}'`
cecho "$LINE_COLOR" red
else
! echo "$LINE" | grep -q "$FTPPROMPT"
! if [ $? == 0 ]; then
! echo -n "$LINE" | awk -F">" '{printf("%s>",$1)}'
! LINE_COLOR=`echo "$LINE" | awk -F">" '{print $2}'`
! cecho "$LINE_COLOR" green
! else
! echo "$LINE"
! fi
fi
done <"$LOG" | less -R -N
./readlog_with_ftp log
これでひとまず、落ち着きました。
ちなみに perl だと Term::ANSIColor というモジュールがあり、とっても簡単にカラー出力ができます。perldoc -m Term::ANSIColor またはCPANのページをご参照あれ。つーか最初から perl でやればよかった…orz.
追記:
tput を使用したfumiyas ししょーのサンプルスクリプト結果です。
昔の放送終了時のTVみたいだ・・・(^_^;






コレを使えばいいのでは。。。
http://term-ansicolor.rubyforge.org/
投稿: anaito | 2007年2月28日 (水) 12:50
anaitoさん、情報ありがとうございます。
おっしゃる通りです。perlと同名で既にモジュールがあったんですね。また車輪の再発明をしてしまった・・・(T_T)
rubyforgeをもっと見るようにします。
投稿: kyagi | 2007年2月28日 (水) 13:18
#!bin/sh
##
## Shell Demo: tput(1) and colors
## See also terminfo(5)...
##
## Author: SATOH Fumiyasu @ OSSTech
cn=`tput colors`
cm=`expr $cn - 1`
c="`seq 0 $cm`"
for f in $c; do
for b in $c; do
tput setaf $f
tput setab $b
echo "f=$f b=$b"
done
done
tput setb 0
投稿: fumiyas | 2007年3月 1日 (木) 02:40
fumiyasししょー、コメントありがとうございます。
サンプルスクリプトの結果を追記させていただきました。tput勉強します。
投稿: kyagi | 2007年3月 1日 (木) 10:44
最後の行は「tput setab 0」の間違いだった。
投稿: fumiyas | 2007年3月 1日 (木) 11:26