`cat *.log > merge.log` で済むものをわざわざRubyで書く。


## 背景
perlでログ出力する際に、csv形式、UTF-8で出力を行っていた。
そのため、cp932にエンコードしないとMS Officeで扱えない。。

## 目的
ログファイルをひとまとめにし、且つcp932で出力しMS Officeで扱えるようにする。
、、、本当は、色々とネタを増やしたかった。

## 作り始めてからの心情
ファイルまとめるくらい、すぐ作れるわ。ついでにtsvでも出力したろ。
　↓
できた！、、、あれ、Windowsで動かないやん。。
　↓
できた！、、、UTF-8のままじゃExcelでそのまま開けないやん。。
　↓
できた！、、、ここまできたら、勉強で色々入れ込もう。
　↓
やべぇ出来た。
　↓
Qiitaに載せとこ！、、、え、ワンライナーでできるって？！うそだーーーーーーーーーーーー！！

### [__hageさんのコメント](#comment-1e4b18b82e5f98594818)より
```ruby -r nkf -e 'puts NKF.nkf("-s", ARGF.to_a.join)' (ログファイルがあるフォルダ)/* > (出力先ファイル名)```

マジだ。。。

## 特長
WindowsでもMacでもLinuxでも同じ結果を返すはず。
というかWindowsが特殊説。

## ソース
実行の方法
```ruby combine_logfile.rb [-t] "ログファイルがあるフォルダ" "結合したログファイルの出力先ファイル名"```


```ruby:combine_logfile.rb
require 'fileutils'
require 'optparse'
require 'securerandom'

# あえてのオープンクラス
class String
  # 引数用
  def to_multi_pf_arg
    windows? ? self.gsub(File::ALT_SEPARATOR) {File::SEPARATOR} : self
  end

  # 文字列用
  def to_multi_pf_str
    windows? ? self.encode('cp932') : self
  end

  private
    # OS が windows か判定
    def windows?
      RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|cygwin|bccwin/
    end
end

class FileManager
  # コンストラクタ
  def initialize(dir)
    @dir ||= complete_slash(dir)
  end

  # ログファイルの結合
  def combine_logfile(save_filename)
    # すでに保存先にファイルがあったら、削除
    FileUtils.rm save_filename if File.exist? save_filename
    # 追記モードで保存先ファイルを開く
    File.open(save_filename, 'a') do |write_f|
      # 配列から順々にファイルを読み込み追記
      list_logfile.each do |read_f|
        p "Reading File: #{read_f.to_s}"
        write_f << File.read(read_f, encoding: 'UTF-8')
      end
    end
    # MS Officeで扱える文字コードに変換
    encode_to_cp932 save_filename
    p "Save to #{save_filename}"
  end

  # csv => tsv 変換
  def to_tsv(save_filename)
    tmp_filename = to_tmpfile(save_filename)
    # comma を tab に置換
    File.write save_filename, File.read(tmp_filename).gsub(',', "\t")
    # 作業ファイルを削除
    FileUtils.rm tmp_filename
  end

  private
      # パスの末尾スラッシュ補完
      def complete_slash(dir)
        complete_slash_path = dir
        if dir !~ /\/$/
          complete_slash_path = dir + '/'
        end
        complete_slash_path
      end

      # .log ファイルを再帰的に検索
      def list_logfile
        # Dir.globメソッドで再帰的にチェックするためのおまじない
        base_dir = @dir + '**/*.log'
        Dir.glob(base_dir)
      end

      # MS Officeで扱える文字コードに変換
      def encode_to_cp932(save_filename)
        tmp_filename = to_tmpfile(save_filename)
        # utf-8 => cp932 に変換
        File.write(save_filename, File.read(tmp_filename, encoding: 'UTF-8'),
                   encoding: 'cp932:UTF-8')
        # 作業ファイルを削除
        FileUtils.rm tmp_filename
      end

      def to_tmpfile(filename)
        # 作業ファイル用の乱数を生成
        rand = SecureRandom.hex(6)
        tmp_filename = filename + '.' + rand
        FileUtils.mv filename, tmp_filename
        tmp_filename
      end
end

# コマンドラインでオプションを利用しやすくするためのインスタンスを生成
opt  = OptionParser.new
# オプションを格納するためのハッシュ
OPTS = {}

# -t オプション設定
opt.on('-t') {|v| OPTS[:tsv] = v }
# オプションを配列から外す
opt.parse!(ARGV)

# 引数が2つなければエラー
if ARGV.size < 2
  raise '引数が足りません。ログファイル格納場所, 結合したログファイル格納先 を指定して下さい'.to_multi_pf_str
end

# 第1引数: ログファイルのパス指定
target_dir    = ARGV[0].to_s.to_multi_pf_arg
# 第2引数: 結合したログファイルのパス指定
save_filename = ARGV[1].to_s.to_multi_pf_arg

# FileManagerのインスタンス生成
file_mgr = FileManager.new(target_dir)
# ログファイルの結合
file_mgr.combine_logfile save_filename

# '-t' オプションがあれば、tsvで出力
if OPTS[:tsv]
  file_mgr.to_tsv save_filename
end
```

## 実際書いてみて
WindowsとMacの差を埋めるのつらたん。
もっと汎用的に使える物作りたい。。