RubyでNagiosのアラートメールを解析するスクリプトを書いてみた。
文字コード処理の話:
Rubyでは、
などとすることにより、任意の文字コードのテキストファイルを、適切な(内部文字コード?)
文字コードに変換して、読み込むことができるようだ。
として、変換後の文字コードを明示的に指定することもできる。
しかし、いずれにせよ、テキストファイルの文字コードは事前に分かっていなければならず、
自動的な文字コード変換をやってくれるわけではない。
(「ファイルの文字コード」の部分に、自動判別を指定するようなキーワードを
記述することもできるのだろうか?)
とすると、今回のケースのように、テキストファイルがUTF-8なのかShift_JISなのか分からない
ような場合、この方法は使えない。
よって、ファイルデータを読み込んだ後に、個別に文字コードを変換する方法を試みた。
とすると、読み取った一行について、文字コード変換を行うことができる。
こうして実施してみたところ、正規表現マッチングの部分でエラーが起こった。
どうやら、この方法は、適切な文字コードになった文字列についてしか使えないようだ。
とすると、不適切な文字コードで読み込まれた文字列の文字コードを正しく設定しなおすことが
できるらしい。この場合、Shift_JISのファイルを読み込んだ場合におかしくなるわけだから、
で、正しく動作させることは可能だろう。しかし、いずれにせよ、自動判別をさせることはできない。
(あるいは、この場合も、line.force_encodingに、自動判別を指定するようなキーワードの指定
が可能なのだろうか?)
結局、Rubyでは、NKFの機能を利用できるようなので、これを用いて実現することとした。
とすると、読み込んだテキストがUTF-8であろうが、Shift_JISであろうが、UTF-8に変換し、
その後の処理を正しく行うことができた。まあ、
ruby|perl - 文字コードのちょっと高度な判定
の記事などを見ても、自動判別などはあまりすべきではないのだろうなとは思うが。。
所感:
主にJavaやPHPと比較して、Rubyの良いなと思ったところ
1.ソースの文字コードを、ソースコード上で宣言することができる
大したことではないのだろうけど、だからこそ、こういうのは出来て欲しいと思う。
上にも書いたが、基本的に文字コードの自動判別などはあまりすべきでないわけだし、
そのソースコードがどの文字コードで書かれたものかなんて、中身を検証しないでも、
できるようになっていて欲しいもの。
2.メソッドにブロックを渡せること
Rubyの文法書をさらっと見ただけではピンと来なかったが、FileやBenchmarkを使ってみて、
なるほど便利だ!と思った。今後、自分でも使いこなせるようにしていきたいと思う。
3.正規表現マッチングの記述
Perlを知ってる人にとっては当たり前なのかもしれないが、簡単に分かりやすく書けて、
良いなと思った。Rubyの文法を見ていて、同じことが色んな書き方で書けるというのは
あまり良い点とは思わなかったが、これに関しては、積極的に使いたいと思える。
今のところは、そんな感じ。
もう少し、ソースコードを綺麗にブログに載っけれたら良いなと思うので、
次は、そういうツールを作ってみよう。
1: # coding: utf-8
2:
3: require "nkf"
4: require "date"
5: require 'benchmark'
6:
7: class AlertMailParser
8:
9: def initialize(fname, file)
10: @fname = fname
11: parse(file)
12: end
13:
14: # ファイルからテキストを入力し、アラート情報をインスタンス変数に設定
15: def parse(file)
16:
17: # additionalフラグをfalseで初期化
18: additional = false
19: @additional_info = ""
20:
21: # ファイルの一行毎に処理
22: file.each {|line|
23:
24: # 取得した行をUTF-8に変換
25: # 「line = line.encode("UTF-8")」では、Shift_JISファイルを読み込んだ際にエラーとなった
26: line = NKF.nkf("-w",line)
27:
28: # additionalフラグがたっていれば、取得行を追記して次行へ
29: if additional
30: if line.strip.length > 0
31: @additional_info += line
32: end
33: next
34: else
35: line = line.strip
36: end
37:
38: # additionalフラグがたっていなければ、それぞれマッチするインスタンス変数にセット
39:
40: if /^Subject:/ =~ line
41: @subject = $'.strip
42: end
43:
44: if /^Notification Type:/u =~ line
45: @notification_type = $'.strip
46: end
47:
48: if /^Service:/ =~ line
49: @service = $'.strip
50: end
51:
52: if /^Host:/ =~ line
53: @host = $'.strip
54: end
55:
56: if /^Address:/ =~ line
57: @address = $'.strip
58: end
59:
60: if /^State:/ =~ line
61: @state = $'.strip
62: end
63:
64: # 日付の場合は、yyyy-mm-dd hh:mm:ssに変換
65: if /^Date\/Time:/ =~ line
66: @datetime = DateTime.parse($'.strip)
67: @datetime = @datetime.strftime("%F %T")
68: end
69:
70: # additionalフラグをたてる
71: if /^Additional Info:/ =~ line
72: additional = true
73: end
74: }
75: @additional_info = @additional_info.strip
76: end
77:
78: # アラート情報をCSVファイルの一行として出力
79: def output(file)
80:
81: # アラート情報をダブルクォートで括り、カンマで連結
82: line = "\"#{@fname}\""
83: line += ",\"#{@subject}\""
84: line += ",\"#{@notification_type}\""
85: line += ",\"#{@service}\""
86: line += ",\"#{@host}\""
87: line += ",\"#{@address}\""
88: line += ",\"#{@state}\""
89: line += ",\"#{@datetime}\""
90: line += ",\"#{@additional_info}\"\n"
91:
92: # Excelで開けるようShift_JISで出力
93: file.write(line.encode("Shift_JIS"))
94: end
95:
96: end
97:
98:
99: # コマンド引数により、メールが格納されたディレクトリを指定
100: mail_dir = ARGV[0]
101:
102: # puts Benchmark::CAPTION
103: # puts Benchmark.measure{
104:
105: # 出力用CSVファイルをオープン
106: # ここで「File.open("output.csv", "w:Shift_JIS")」として、93行目のエンコード指定をはずしても可
107: # 速度はどちらでもあまり変わらなかった
108: File.open("output.csv", "w") {|wf|
109:
110: # ディレクトリ以下のファイル毎に処理
111: Dir.foreach(mail_dir) {|fname|
112:
113: # コンテンツがファイルの時のみ実行
114: if File.ftype(mail_dir + "/" + fname) == "file"
115:
116: # 入力用ファイルをオープン
117: # Shift_JISとUTF-8と両方のファイルがあるので、ここで指定はできない
118: File.open(mail_dir + "/" + fname, "r") {|rf|
119: parser = AlertMailParser.new(fname, rf)
120: parser.output(wf)
121: }
122: end
123: }
124: }
125:
126: # }
文字コード処理の話:
Rubyでは、
File.open("ファイル名","r:ファイルの文字コード")などとすることにより、任意の文字コードのテキストファイルを、適切な(内部文字コード?)
文字コードに変換して、読み込むことができるようだ。
File.open("ファイル名","r:ファイルの文字コード:変換後の文字コード")として、変換後の文字コードを明示的に指定することもできる。
しかし、いずれにせよ、テキストファイルの文字コードは事前に分かっていなければならず、
自動的な文字コード変換をやってくれるわけではない。
(「ファイルの文字コード」の部分に、自動判別を指定するようなキーワードを
記述することもできるのだろうか?)
とすると、今回のケースのように、テキストファイルがUTF-8なのかShift_JISなのか分からない
ような場合、この方法は使えない。
よって、ファイルデータを読み込んだ後に、個別に文字コードを変換する方法を試みた。
line.encode("変換後の文字コード")とすると、読み取った一行について、文字コード変換を行うことができる。
こうして実施してみたところ、正規表現マッチングの部分でエラーが起こった。
どうやら、この方法は、適切な文字コードになった文字列についてしか使えないようだ。
line.force_encoding("ファイルの文字コード")とすると、不適切な文字コードで読み込まれた文字列の文字コードを正しく設定しなおすことが
できるらしい。この場合、Shift_JISのファイルを読み込んだ場合におかしくなるわけだから、
line.force_encoding("Shift_JIS")
line.encode("UTF-8")で、正しく動作させることは可能だろう。しかし、いずれにせよ、自動判別をさせることはできない。
(あるいは、この場合も、line.force_encodingに、自動判別を指定するようなキーワードの指定
が可能なのだろうか?)
結局、Rubyでは、NKFの機能を利用できるようなので、これを用いて実現することとした。
line = NKF.nkf("-w",line)とすると、読み込んだテキストがUTF-8であろうが、Shift_JISであろうが、UTF-8に変換し、
その後の処理を正しく行うことができた。まあ、
ruby|perl - 文字コードのちょっと高度な判定
の記事などを見ても、自動判別などはあまりすべきではないのだろうなとは思うが。。
所感:
主にJavaやPHPと比較して、Rubyの良いなと思ったところ
1.ソースの文字コードを、ソースコード上で宣言することができる
大したことではないのだろうけど、だからこそ、こういうのは出来て欲しいと思う。
上にも書いたが、基本的に文字コードの自動判別などはあまりすべきでないわけだし、
そのソースコードがどの文字コードで書かれたものかなんて、中身を検証しないでも、
できるようになっていて欲しいもの。
2.メソッドにブロックを渡せること
Rubyの文法書をさらっと見ただけではピンと来なかったが、FileやBenchmarkを使ってみて、
なるほど便利だ!と思った。今後、自分でも使いこなせるようにしていきたいと思う。
3.正規表現マッチングの記述
Perlを知ってる人にとっては当たり前なのかもしれないが、簡単に分かりやすく書けて、
良いなと思った。Rubyの文法を見ていて、同じことが色んな書き方で書けるというのは
あまり良い点とは思わなかったが、これに関しては、積極的に使いたいと思える。
今のところは、そんな感じ。
もう少し、ソースコードを綺麗にブログに載っけれたら良いなと思うので、
次は、そういうツールを作ってみよう。