容量がいっぱいなときに使うduコマンドが使いにくいから、rubyでラッピングしてみた

# 結論
duコマンドをrubyでラッピングしたスクリプトを作りました。

`expand.rb`という名前を作って、下のスクリプトを貼って、以下のように実行します。

```sh
# ruby expand.rb (調べたいパス)
$ ruby expand_du.rb /var/lib/
    7GB /var/lib/
    3GB /var/lib/jenkins
    2GB /var/lib/docker
    1GB /var/lib/mysql
    ...
    4KB /var/lib/mysql-files
du_-k_--max-depth_1_var_lib__20200516231438.logに保存しました
```

スクリプト([github](https://github.com/setsumaru1992/portableScripts/blob/master/frequent_use_codes/ruby_scripts_with_unix_command/expand_du.rb))

```ruby
# 使い方
# ruby expand_du.rb パス
# 例：ruby expand_du.rb ~
#
# 効果
# - パスに対してduコマンドを実行
# - 実行結果を標準出力とファイルに吐き出す
#
# 引数
# - パス: duコマンドを行う対象パス

require 'rbconfig'

def main(target_path)
  return puts "ArgumentError: パスを引数に入れてください" if target_path.nil?

  max_depth_option_str = if os == :macosx
    "-d"
  else
    "--max-depth"
  end

  exec_command = "du -k #{max_depth_option_str} 1 #{target_path}"
  du_result_str = `#{exec_command}`

  return if du_result_str.empty?

  output_disksizes(du_result_str, exec_command)
end

def output_disksizes(du_result_str, exec_command)
  disk_usages = du_result_str
                  .split("\n")
                  .map{|du_result| DiskUsage.new(du_result)}
                  .sort{|x, y| x.size <=> y.size}.reverse

  output_filename = "#{exec_command.gsub(/( |\/){1,}/, "_")}_#{Time.new.strftime("%Y%m%d%H%M%S")}.log"
  output_file = File.open(output_filename, "w")

  disk_usages.each do |disk_usage|
    puts disk_usage.to_s
    output_file.puts(disk_usage.to_s)
  end

  output_file.close
  puts "#{output_filename}に保存しました"
end

class DiskUsage
  attr_reader :size, :path
  def initialize(du_result_line)
    du_result_params = du_result_line.split(" ").map(&:strip)
    @size = du_result_params[0].to_i
    @humanreadable_size, @humanreadable_unit = calc_humanreadable_size
    @path = du_result_params[1]
  end

  def to_s
    # NOTE とりあえず5桁を指定。必要になったら増やす
    "#{sprintf("%5d" % @humanreadable_size)}#{@humanreadable_unit} #{@path}"
  end

  def humanreadable_size_with_unit
    "#{@humanreadable_size}#{@humanreadable_unit}"
  end

  private

  def calc_humanreadable_size
    return [@size, "KB"] if mb_size < 1
    return [mb_size, "MB"] if gb_size < 1
    return [gb_size, "GB"] if tb_size < 1
    [tb_size, "TB"]
  end

  def kb_size
    @size
  end

  def mb_size
    kb_size.fdiv(1024).round(1)
  end

  def gb_size
    mb_size.fdiv(1024).round(1)
  end

  def tb_size
    gb_size.fdiv(1024).round(1)
  end
end

def os
  case RbConfig::CONFIG['host_os']
  when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
    :windows
  when /darwin|mac os/
    :macosx
  when /linux/
    :linux
  when /solaris|bsd/
    :unix
  else
    :unknown
  end
end
```



# 経緯
レンタルサーバやMacの容量がいっぱいになったときに、どのフォルダが原因なのか知りたくなります。

そこでMacならストレージ使用状況を見たり、レンタルサーバでもdfコマンドとかで全体のどれくらいを使っているかはわかります。しかし全体がわかるだけでどのフォルダが原因かわかりません。

そこでターミナルを使える人が使えるのはduコマンドです（lsコマンドも候補にあがるんですが、lsコマンドは直下のファイルの大きさはわかるんですが、フォルダがどれだけ大きいかはわかりません）。

しかしこのduコマンドなんですが、UNIXコマンドであるせいか、ちょっといまいちなんですよね。

## ソートがいまいち
読みやすくするのに -h オプションをつけて、sortコマンドを使ってサイズを降順で出すんですが、そうすると5KBが4GBよりも上に来るんですよね。数字だけ見ているので仕方ないんですが。

## 記録するのに2回打つのが面倒くさい
duを使うと標準出力に結果を出してくれるけれど、出したあと結局結果を記録したくなります。最初からファイル出力すればいいんですが、ファイル出力してからcatするのが面倒なんですよね。工夫してコマンド打てばファイルにも標準出力にも出してくれるんですが、duコマンド必要なときって大抵急いでいるので、楽にやりたかったんです。

# スクリプトの特徴
↑の問題を解決したスクリプトが最初に載せたスクリプトです。

特徴は次のようになります。

- 単位を考えた上でソートしてくれる
- 標準出力にもファイルにも出してくれる（＆どのフォルダを調べたかわかるファイル名で出力してくれる）

完全に自分用に作ったんですが、同じことに困っている方がいらっしゃったら使ってみてください。